3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4 * Copyright (C) 2002-2004 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
15 * $Id: skinie.php,v 1.1.1.1 2005-02-28 07:14:53 kimitake Exp $
20 // hardcoded value (see constructor). When 1, interesting info about the
21 // parsing process is sent to the output
24 // parser/file pointer
28 // which data has been read?
37 // to maintain track of where we are inside the XML file
50 * constructor initializes data structures
52 function SKINIMPORT() {
53 // disable magic_quotes_runtime if it's turned on
54 set_magic_quotes_runtime(0);
65 xml_parser_free($this->parser);
70 // which data has been read?
71 $this->metaDataRead = 0;
74 // to maintain track of where we are inside the XML file
79 $this->inTemplate = 0;
80 $this->currentName = '';
81 $this->currentPartName = '';
83 // character data pile
86 // list of skinnames and templatenames (will be array of array)
87 $this->skins = array();
88 $this->templates = array();
90 // extra info included in the XML files (e.g. installation notes)
94 $this->parser = xml_parser_create();
95 xml_set_object($this->parser, $this);
96 xml_set_element_handler($this->parser, 'startElement', 'endElement');
97 xml_set_character_data_handler($this->parser, 'characterData');
98 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
103 * Reads an XML file into memory
108 * Set to 1 when only the metadata needs to be read (optional, default 0)
110 function readFile($filename, $metaOnly = 0) {
112 $this->fp = @fopen($filename, 'r');
113 if (!$this->fp) return 'Failed to open file/URL';
118 while (!feof($this->fp)) {
119 $tempbuffer .= fread($this->fp, 4096);
124 [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates
125 with CDATA sections. need to investigate consequences.
126 see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
128 // backwards compatibility with the non-wellformed skinbackup.xml files
129 // generated by v2/v3 (when CDATA sections were present in skins)
130 // split up those CDATA sections into multiple ones
131 $tempbuffer = preg_replace_callback(
132 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
135 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
141 fwrite($temp, $tempbuffer);
144 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
145 $err = xml_parse( $this->parser, $buffer, feof($temp) );
146 if (!$err && $this->debug)
147 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />';
156 * Returns the list of skin names
158 function getSkinNames() {
159 return array_keys($this->skins);
163 * Returns the list of template names
165 function getTemplateNames() {
166 return array_keys($this->templates);
170 * Returns the extra information included in the XML file
177 * Writes the skins and templates to the database
179 * @param $allowOverwrite
180 * set to 1 when allowed to overwrite existing skins with the same name
183 function writeToDatabase($allowOverwrite = 0) {
184 $existingSkins = $this->checkSkinNameClashes();
185 $existingTemplates = $this->checkTemplateNameClashes();
187 // if not allowed to overwrite, check if any nameclashes exists
188 if (!$allowOverwrite) {
189 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0))
190 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite';
193 foreach ($this->skins as $skinName => $data) {
194 // 1. if exists: delete all part data, update desc data
195 // if not exists: create desc
196 if (in_array($skinName, $existingSkins)) {
197 $skinObj = SKIN::createFromName($skinName);
199 // delete all parts of the skin
200 $skinObj->deleteAllParts();
202 // update general info
203 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
205 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
206 $skinObj = new SKIN($skinid);
210 foreach ($data['parts'] as $partName => $partContent) {
211 $skinObj->update($partName, $partContent);
215 foreach ($this->templates as $templateName => $data) {
216 // 1. if exists: delete all part data, update desc data
217 // if not exists: create desc
218 if (in_array($templateName, $existingTemplates)) {
219 $templateObj = TEMPLATE::createFromName($templateName);
221 // delete all parts of the template
222 $templateObj->deleteAllParts();
224 // update general info
225 $templateObj->updateGeneralInfo($templateName, $data['description']);
227 $templateid = TEMPLATE::createNew($templateName, $data['description']);
228 $templateObj = new TEMPLATE($templateid);
232 foreach ($data['parts'] as $partName => $partContent) {
233 $templateObj->update($partName, $partContent);
241 * returns an array of all the skin nameclashes (empty array when no name clashes)
243 function checkSkinNameClashes() {
246 foreach ($this->skins as $skinName => $data) {
247 if (SKIN::exists($skinName))
248 array_push($clashes, $skinName);
255 * returns an array of all the template nameclashes
256 * (empty array when no name clashes)
258 function checkTemplateNameClashes() {
261 foreach ($this->templates as $templateName => $data) {
262 if (TEMPLATE::exists($templateName))
263 array_push($clashes, $templateName);
270 * Called by XML parser for each new start element encountered
272 function startElement($parser, $name, $attrs) {
273 if ($this->debug) echo 'START: ', $name, '<br />';
286 if (!$this->inMeta) {
288 $this->currentName = $attrs['name'];
289 $this->skins[$this->currentName]['type'] = $attrs['type'];
290 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
291 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
292 $this->skins[$this->currentName]['parts'] = array();
294 $this->skins[$attrs['name']] = array();
295 $this->skins[$attrs['name']]['parts'] = array();
299 if (!$this->inMeta) {
300 $this->inTemplate = 1;
301 $this->currentName = $attrs['name'];
302 $this->templates[$this->currentName]['parts'] = array();
304 $this->templates[$attrs['name']] = array();
305 $this->templates[$attrs['name']]['parts'] = array();
312 $this->currentPartName = $attrs['name'];
315 echo 'UNEXPECTED TAG: ' , $name , '<br />';
319 // character data never contains other tags
320 $this->clearCharacterData();
325 * Called by the XML parser for each closing tag encountered
327 function endElement($parser, $name) {
328 if ($this->debug) echo 'END: ', $name, '<br />';
337 $this->metaDataRead = 1;
340 $this->info = $this->getCharacterData();
342 if (!$this->inMeta) $this->inSkin = 0;
345 if (!$this->inMeta) $this->inTemplate = 0;
349 $this->skins[$this->currentName]['description'] = $this->getCharacterData();
351 $this->templates[$this->currentName]['description'] = $this->getCharacterData();
356 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
358 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
362 echo 'UNEXPECTED TAG: ' , $name, '<br />';
365 $this->clearCharacterData();
370 * Called by XML parser for data inside elements
372 function characterData ($parser, $data) {
373 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />';
374 $this->cdata .= $data;
378 * Returns the data collected so far
380 function getCharacterData() {
385 * Clears the data buffer
387 function clearCharacterData() {
392 * Static method that looks for importable XML files in subdirs of the given dir
394 function searchForCandidates($dir) {
395 $candidates = array();
397 $dirhandle = opendir($dir);
398 while ($filename = readdir($dirhandle)) {
399 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
400 $xml_file = $dir . $filename . '/skinbackup.xml';
401 if (file_exists($xml_file) && is_readable($xml_file)) {
402 $candidates[$filename] = $filename; //$xml_file;
405 // backwards compatibility
406 $xml_file = $dir . $filename . '/skindata.xml';
407 if (file_exists($xml_file) && is_readable($xml_file)) {
408 $candidates[$filename] = $filename; //$xml_file;
412 closedir($dirhandle);
429 * Constructor initializes data structures
431 function SKINEXPORT() {
432 // list of templateIDs to export
433 $this->templates = array();
435 // list of skinIDs to export
436 $this->skins = array();
438 // extra info to be in XML file
443 * Adds a template to be exported
447 * @result false when no such ID exists
449 function addTemplate($id) {
450 if (!TEMPLATE::existsID($id)) return 0;
452 $this->templates[$id] = TEMPLATE::getNameFromId($id);
458 * Adds a skin to be exported
462 * @result false when no such ID exists
464 function addSkin($id) {
465 if (!SKIN::existsID($id)) return 0;
467 $this->skins[$id] = SKIN::getNameFromId($id);
473 * Sets the extra info to be included in the exported file
475 function setInfo($info) {
481 * Outputs the XML contents of the export file
484 * set to 0 if you don't want to send out headers
485 * (optional, default 1)
487 function export($setHeaders = 1) {
489 // make sure the mimetype is correct, and that the data does not show up
490 // in the browser, but gets saved into and XML file (popup download window)
491 header('Content-Type: text/xml');
492 header('Content-Disposition: attachment; filename="skinbackup.xml"');
493 header('Expires: 0');
494 header('Pragma: no-cache');
497 echo "<nucleusskin>\n";
502 foreach ($this->skins as $skinId => $skinName) {
503 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n";
506 foreach ($this->templates as $templateId => $templateName) {
507 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n";
511 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n";
512 echo "\t</meta>\n\n\n";
515 foreach ($this->skins as $skinId => $skinName) {
516 $skinId = intval($skinId);
517 $skinObj = new SKIN($skinId);
519 echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n";
521 echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n";
523 $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId);
524 while ($partObj = mysql_fetch_object($res)) {
525 echo "\t\t",'<part name="',htmlspecialchars($partObj->stype),'">';
526 echo '<![CDATA[', $this->escapeCDATA($partObj->scontent),']]>';
530 echo "\t</skin>\n\n\n";
533 // contents templates
534 foreach ($this->templates as $templateId => $templateName) {
535 $templateId = intval($templateId);
537 echo "\t",'<template name="',htmlspecialchars($templateName),'">',"\n";
539 echo "\t\t",'<description>',htmlspecialchars(TEMPLATE::getDesc($templateId)),'</description>',"\n";
541 $res = sql_query('SELECT tpartname, tcontent FROM '.sql_table('template').' WHERE tdesc='.$templateId);
542 while ($partObj = mysql_fetch_object($res)) {
543 echo "\t\t",'<part name="',htmlspecialchars($partObj->tpartname),'">';
544 echo '<![CDATA[', $this->escapeCDATA($partObj->tcontent) ,']]>';
545 echo '</part>',"\n\n";
548 echo "\t</template>\n\n\n";
551 echo '</nucleusskin>';
555 * Escapes CDATA content so it can be included in another CDATA section
557 function escapeCDATA($cdata)
559 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);