3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
\r
4 * Copyright (C) 2002-2004 The Nucleus Group
\r
6 * This program is free software; you can redistribute it and/or
\r
7 * modify it under the terms of the GNU General Public License
\r
8 * as published by the Free Software Foundation; either version 2
\r
9 * of the License, or (at your option) any later version.
\r
10 * (see nucleus/documentation/index.html#license for more info)
\r
12 * This class contains two classes that can be used for importing and
\r
13 * exporting Nucleus skins: SKINIMPORT and SKINEXPORT
\r
18 // hardcoded value (see constructor). When 1, interesting info about the
\r
19 // parsing process is sent to the output
\r
22 // parser/file pointer
\r
26 // which data has been read?
\r
35 // to maintain track of where we are inside the XML file
\r
42 var $currentPartName;
\r
48 * constructor initializes data structures
\r
50 function SKINIMPORT() {
\r
51 // disable magic_quotes_runtime if it's turned on
\r
52 set_magic_quotes_runtime(0);
\r
63 xml_parser_free($this->parser);
\r
68 // which data has been read?
\r
69 $this->metaDataRead = 0;
\r
72 // to maintain track of where we are inside the XML file
\r
77 $this->inTemplate = 0;
\r
78 $this->currentName = '';
\r
79 $this->currentPartName = '';
\r
81 // character data pile
\r
84 // list of skinnames and templatenames (will be array of array)
\r
85 $this->skins = array();
\r
86 $this->templates = array();
\r
88 // extra info included in the XML files (e.g. installation notes)
\r
92 $this->parser = xml_parser_create();
\r
93 xml_set_object($this->parser, $this);
\r
94 xml_set_element_handler($this->parser, 'startElement', 'endElement');
\r
95 xml_set_character_data_handler($this->parser, 'characterData');
\r
96 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
\r
101 * Reads an XML file into memory
\r
104 * Which file to read
\r
106 * Set to 1 when only the metadata needs to be read (optional, default 0)
\r
108 function readFile($filename, $metaOnly = 0) {
\r
110 $this->fp = @fopen($filename, 'r');
\r
111 if (!$this->fp) return 'Failed to open file/URL';
\r
116 while (!feof($this->fp)) {
\r
117 $tempbuffer .= fread($this->fp, 4096);
\r
122 [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates
\r
123 with CDATA sections. need to investigate consequences.
\r
124 see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
\r
126 // backwards compatibility with the non-wellformed skinbackup.xml files
\r
127 // generated by v2/v3 (when CDATA sections were present in skins)
\r
128 // split up those CDATA sections into multiple ones
\r
129 $tempbuffer = preg_replace_callback(
\r
130 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
\r
133 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
\r
139 fwrite($temp, $tempbuffer);
\r
142 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
\r
143 $err = xml_parse( $this->parser, $buffer, feof($temp) );
\r
144 if (!$err && $this->debug)
\r
145 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />';
\r
154 * Returns the list of skin names
\r
156 function getSkinNames() {
\r
157 return array_keys($this->skins);
\r
161 * Returns the list of template names
\r
163 function getTemplateNames() {
\r
164 return array_keys($this->templates);
\r
168 * Returns the extra information included in the XML file
\r
170 function getInfo() {
\r
171 return $this->info;
\r
175 * Writes the skins and templates to the database
\r
177 * @param $allowOverwrite
\r
178 * set to 1 when allowed to overwrite existing skins with the same name
\r
181 function writeToDatabase($allowOverwrite = 0) {
\r
182 $existingSkins = $this->checkSkinNameClashes();
\r
183 $existingTemplates = $this->checkTemplateNameClashes();
\r
185 // if not allowed to overwrite, check if any nameclashes exists
\r
186 if (!$allowOverwrite) {
\r
187 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0))
\r
188 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite';
\r
191 foreach ($this->skins as $skinName => $data) {
\r
192 // 1. if exists: delete all part data, update desc data
\r
193 // if not exists: create desc
\r
194 if (in_array($skinName, $existingSkins)) {
\r
195 $skinObj = SKIN::createFromName($skinName);
\r
197 // delete all parts of the skin
\r
198 $skinObj->deleteAllParts();
\r
200 // update general info
\r
201 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
\r
203 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);
\r
204 $skinObj = new SKIN($skinid);
\r
208 foreach ($data['parts'] as $partName => $partContent) {
\r
209 $skinObj->update($partName, $partContent);
\r
213 foreach ($this->templates as $templateName => $data) {
\r
214 // 1. if exists: delete all part data, update desc data
\r
215 // if not exists: create desc
\r
216 if (in_array($templateName, $existingTemplates)) {
\r
217 $templateObj = TEMPLATE::createFromName($templateName);
\r
219 // delete all parts of the template
\r
220 $templateObj->deleteAllParts();
\r
222 // update general info
\r
223 $templateObj->updateGeneralInfo($templateName, $data['description']);
\r
225 $templateid = TEMPLATE::createNew($templateName, $data['description']);
\r
226 $templateObj = new TEMPLATE($templateid);
\r
230 foreach ($data['parts'] as $partName => $partContent) {
\r
231 $templateObj->update($partName, $partContent);
\r
239 * returns an array of all the skin nameclashes (empty array when no name clashes)
\r
241 function checkSkinNameClashes() {
\r
242 $clashes = array();
\r
244 foreach ($this->skins as $skinName => $data) {
\r
245 if (SKIN::exists($skinName))
\r
246 array_push($clashes, $skinName);
\r
253 * returns an array of all the template nameclashes
\r
254 * (empty array when no name clashes)
\r
256 function checkTemplateNameClashes() {
\r
257 $clashes = array();
\r
259 foreach ($this->templates as $templateName => $data) {
\r
260 if (TEMPLATE::exists($templateName))
\r
261 array_push($clashes, $templateName);
\r
268 * Called by XML parser for each new start element encountered
\r
270 function startElement($parser, $name, $attrs) {
\r
271 if ($this->debug) echo 'START: ', $name, '<br />';
\r
274 case 'nucleusskin':
\r
281 // no action needed
\r
284 if (!$this->inMeta) {
\r
286 $this->currentName = $attrs['name'];
\r
287 $this->skins[$this->currentName]['type'] = $attrs['type'];
\r
288 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
\r
289 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
\r
290 $this->skins[$this->currentName]['parts'] = array();
\r
292 $this->skins[$attrs['name']] = array();
\r
293 $this->skins[$attrs['name']]['parts'] = array();
\r
297 if (!$this->inMeta) {
\r
298 $this->inTemplate = 1;
\r
299 $this->currentName = $attrs['name'];
\r
300 $this->templates[$this->currentName]['parts'] = array();
\r
302 $this->templates[$attrs['name']] = array();
\r
303 $this->templates[$attrs['name']]['parts'] = array();
\r
306 case 'description':
\r
307 // no action needed
\r
310 $this->currentPartName = $attrs['name'];
\r
313 echo 'UNEXPECTED TAG: ' , $name , '<br />';
\r
317 // character data never contains other tags
\r
318 $this->clearCharacterData();
\r
323 * Called by the XML parser for each closing tag encountered
\r
325 function endElement($parser, $name) {
\r
326 if ($this->debug) echo 'END: ', $name, '<br />';
\r
329 case 'nucleusskin':
\r
331 $this->allRead = 1;
\r
335 $this->metaDataRead = 1;
\r
338 $this->info = $this->getCharacterData();
\r
340 if (!$this->inMeta) $this->inSkin = 0;
\r
343 if (!$this->inMeta) $this->inTemplate = 0;
\r
345 case 'description':
\r
346 if ($this->inSkin) {
\r
347 $this->skins[$this->currentName]['description'] = $this->getCharacterData();
\r
349 $this->templates[$this->currentName]['description'] = $this->getCharacterData();
\r
353 if ($this->inSkin) {
\r
354 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
\r
356 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
\r
360 echo 'UNEXPECTED TAG: ' , $name, '<br />';
\r
363 $this->clearCharacterData();
\r
368 * Called by XML parser for data inside elements
\r
370 function characterData ($parser, $data) {
\r
371 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />';
\r
372 $this->cdata .= $data;
\r
376 * Returns the data collected so far
\r
378 function getCharacterData() {
\r
379 return $this->cdata;
\r
383 * Clears the data buffer
\r
385 function clearCharacterData() {
\r
390 * Static method that looks for importable XML files in subdirs of the given dir
\r
392 function searchForCandidates($dir) {
\r
393 $candidates = array();
\r
395 $dirhandle = opendir($dir);
\r
396 while ($filename = readdir($dirhandle)) {
\r
397 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
\r
398 $xml_file = $dir . $filename . '/skinbackup.xml';
\r
399 if (file_exists($xml_file) && is_readable($xml_file)) {
\r
400 $candidates[$filename] = $filename; //$xml_file;
\r
403 // backwards compatibility
\r
404 $xml_file = $dir . $filename . '/skindata.xml';
\r
405 if (file_exists($xml_file) && is_readable($xml_file)) {
\r
406 $candidates[$filename] = $filename; //$xml_file;
\r
410 closedir($dirhandle);
\r
412 return $candidates;
\r
427 * Constructor initializes data structures
\r
429 function SKINEXPORT() {
\r
430 // list of templateIDs to export
\r
431 $this->templates = array();
\r
433 // list of skinIDs to export
\r
434 $this->skins = array();
\r
436 // extra info to be in XML file
\r
441 * Adds a template to be exported
\r
445 * @result false when no such ID exists
\r
447 function addTemplate($id) {
\r
448 if (!TEMPLATE::existsID($id)) return 0;
\r
450 $this->templates[$id] = TEMPLATE::getNameFromId($id);
\r
456 * Adds a skin to be exported
\r
460 * @result false when no such ID exists
\r
462 function addSkin($id) {
\r
463 if (!SKIN::existsID($id)) return 0;
\r
465 $this->skins[$id] = SKIN::getNameFromId($id);
\r
471 * Sets the extra info to be included in the exported file
\r
473 function setInfo($info) {
\r
474 $this->info = $info;
\r
479 * Outputs the XML contents of the export file
\r
481 * @param $setHeaders
\r
482 * set to 0 if you don't want to send out headers
\r
483 * (optional, default 1)
\r
485 function export($setHeaders = 1) {
\r
487 // make sure the mimetype is correct, and that the data does not show up
\r
488 // in the browser, but gets saved into and XML file (popup download window)
\r
489 header('Content-Type: text/xml');
\r
490 header('Content-Disposition: attachment; filename="skinbackup.xml"');
\r
491 header('Expires: 0');
\r
492 header('Pragma: no-cache');
\r
495 echo "<nucleusskin>\n";
\r
500 foreach ($this->skins as $skinId => $skinName) {
\r
501 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n";
\r
504 foreach ($this->templates as $templateId => $templateName) {
\r
505 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n";
\r
509 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n";
\r
510 echo "\t</meta>\n\n\n";
\r
513 foreach ($this->skins as $skinId => $skinName) {
\r
514 $skinId = intval($skinId);
\r
515 $skinObj = new SKIN($skinId);
\r
517 echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n";
\r
519 echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n";
\r
521 $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId);
\r
522 while ($partObj = mysql_fetch_object($res)) {
\r
523 echo "\t\t",'<part name="',htmlspecialchars($partObj->stype),'">';
\r
524 echo '<![CDATA[', $this->escapeCDATA($partObj->scontent),']]>';
\r
525 echo "</part>\n\n";
\r
528 echo "\t</skin>\n\n\n";
\r
531 // contents templates
\r
532 foreach ($this->templates as $templateId => $templateName) {
\r
533 $templateId = intval($templateId);
\r
535 echo "\t",'<template name="',htmlspecialchars($templateName),'">',"\n";
\r
537 echo "\t\t",'<description>',htmlspecialchars(TEMPLATE::getDesc($templateId)),'</description>',"\n";
\r
539 $res = sql_query('SELECT tpartname, tcontent FROM '.sql_table('template').' WHERE tdesc='.$templateId);
\r
540 while ($partObj = mysql_fetch_object($res)) {
\r
541 echo "\t\t",'<part name="',htmlspecialchars($partObj->tpartname),'">';
\r
542 echo '<![CDATA[', $this->escapeCDATA($partObj->tcontent) ,']]>';
\r
543 echo '</part>',"\n\n";
\r
546 echo "\t</template>\n\n\n";
\r
549 echo '</nucleusskin>';
\r
553 * Escapes CDATA content so it can be included in another CDATA section
\r
555 function escapeCDATA($cdata)
\r
557 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);
\r