00001 <?php 00002 /* 00003 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 00004 * Copyright (C) 2002-2007 The Nucleus Group 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * (see nucleus/documentation/index.html#license for more info) 00011 */ 00022 class SKINIMPORT { 00023 00024 // hardcoded value (see constructor). When 1, interesting info about the 00025 // parsing process is sent to the output 00026 var $debug; 00027 00028 // parser/file pointer 00029 var $parser; 00030 var $fp; 00031 00032 // which data has been read? 00033 var $metaDataRead; 00034 var $allRead; 00035 00036 // extracted data 00037 var $skins; 00038 var $templates; 00039 var $info; 00040 00041 // to maintain track of where we are inside the XML file 00042 var $inXml; 00043 var $inData; 00044 var $inMeta; 00045 var $inSkin; 00046 var $inTemplate; 00047 var $currentName; 00048 var $currentPartName; 00049 var $cdata; 00050 00051 00052 00056 function SKINIMPORT() { 00057 // disable magic_quotes_runtime if it's turned on 00058 set_magic_quotes_runtime(0); 00059 00060 // debugging mode? 00061 $this->debug = 0; 00062 00063 $this->reset(); 00064 00065 } 00066 00067 function reset() { 00068 if ($this->parser) 00069 xml_parser_free($this->parser); 00070 00071 // XML file pointer 00072 $this->fp = 0; 00073 00074 // which data has been read? 00075 $this->metaDataRead = 0; 00076 $this->allRead = 0; 00077 00078 // to maintain track of where we are inside the XML file 00079 $this->inXml = 0; 00080 $this->inData = 0; 00081 $this->inMeta = 0; 00082 $this->inSkin = 0; 00083 $this->inTemplate = 0; 00084 $this->currentName = ''; 00085 $this->currentPartName = ''; 00086 00087 // character data pile 00088 $this->cdata = ''; 00089 00090 // list of skinnames and templatenames (will be array of array) 00091 $this->skins = array(); 00092 $this->templates = array(); 00093 00094 // extra info included in the XML files (e.g. installation notes) 00095 $this->info = ''; 00096 00097 // init XML parser 00098 $this->parser = xml_parser_create(); 00099 xml_set_object($this->parser, $this); 00100 xml_set_element_handler($this->parser, 'startElement', 'endElement'); 00101 xml_set_character_data_handler($this->parser, 'characterData'); 00102 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 00103 00104 } 00105 00114 function readFile($filename, $metaOnly = 0) { 00115 // open file 00116 $this->fp = @fopen($filename, 'r'); 00117 if (!$this->fp) return 'Failed to open file/URL'; 00118 00119 // here we go! 00120 $this->inXml = 1; 00121 00122 $tempbuffer = null; 00123 00124 while (!feof($this->fp)) { 00125 $tempbuffer .= fread($this->fp, 4096); 00126 } 00127 fclose($this->fp); 00128 00129 /* 00130 [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates 00131 with CDATA sections. need to investigate consequences. 00132 see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA) 00133 00134 // backwards compatibility with the non-wellformed skinbackup.xml files 00135 // generated by v2/v3 (when CDATA sections were present in skins) 00136 // split up those CDATA sections into multiple ones 00137 $tempbuffer = preg_replace_callback( 00138 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms", 00139 create_function( 00140 '$matches', 00141 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];' 00142 ), 00143 $tempbuffer 00144 ); 00145 */ 00146 $temp = tmpfile(); 00147 fwrite($temp, $tempbuffer); 00148 rewind($temp); 00149 00150 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) { 00151 $err = xml_parse( $this->parser, $buffer, feof($temp) ); 00152 if (!$err && $this->debug) 00153 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />'; 00154 } 00155 00156 // all done 00157 $this->inXml = 0; 00158 fclose($temp); 00159 } 00160 00164 function getSkinNames() { 00165 return array_keys($this->skins); 00166 } 00167 00171 function getTemplateNames() { 00172 return array_keys($this->templates); 00173 } 00174 00178 function getInfo() { 00179 return $this->info; 00180 } 00181 00189 function writeToDatabase($allowOverwrite = 0) { 00190 $existingSkins = $this->checkSkinNameClashes(); 00191 $existingTemplates = $this->checkTemplateNameClashes(); 00192 00193 // if not allowed to overwrite, check if any nameclashes exists 00194 if (!$allowOverwrite) { 00195 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) 00196 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite'; 00197 } 00198 00199 foreach ($this->skins as $skinName => $data) { 00200 // 1. if exists: delete all part data, update desc data 00201 // if not exists: create desc 00202 if (in_array($skinName, $existingSkins)) { 00203 $skinObj = SKIN::createFromName($skinName); 00204 00205 // delete all parts of the skin 00206 $skinObj->deleteAllParts(); 00207 00208 // update general info 00209 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']); 00210 } else { 00211 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']); 00212 $skinObj = new SKIN($skinid); 00213 } 00214 00215 // 2. add parts 00216 foreach ($data['parts'] as $partName => $partContent) { 00217 $skinObj->update($partName, $partContent); 00218 } 00219 } 00220 00221 foreach ($this->templates as $templateName => $data) { 00222 // 1. if exists: delete all part data, update desc data 00223 // if not exists: create desc 00224 if (in_array($templateName, $existingTemplates)) { 00225 $templateObj = TEMPLATE::createFromName($templateName); 00226 00227 // delete all parts of the template 00228 $templateObj->deleteAllParts(); 00229 00230 // update general info 00231 $templateObj->updateGeneralInfo($templateName, $data['description']); 00232 } else { 00233 $templateid = TEMPLATE::createNew($templateName, $data['description']); 00234 $templateObj = new TEMPLATE($templateid); 00235 } 00236 00237 // 2. add parts 00238 foreach ($data['parts'] as $partName => $partContent) { 00239 $templateObj->update($partName, $partContent); 00240 } 00241 } 00242 00243 00244 } 00245 00249 function checkSkinNameClashes() { 00250 $clashes = array(); 00251 00252 foreach ($this->skins as $skinName => $data) { 00253 if (SKIN::exists($skinName)) 00254 array_push($clashes, $skinName); 00255 } 00256 00257 return $clashes; 00258 } 00259 00264 function checkTemplateNameClashes() { 00265 $clashes = array(); 00266 00267 foreach ($this->templates as $templateName => $data) { 00268 if (TEMPLATE::exists($templateName)) 00269 array_push($clashes, $templateName); 00270 } 00271 00272 return $clashes; 00273 } 00274 00278 function startElement($parser, $name, $attrs) { 00279 foreach($attrs as $key=>$value) $attrs[$key]=htmlspecialchars($value,ENT_QUOTES); 00280 00281 if ($this->debug) echo 'START: ', htmlspecialchars($name), '<br />'; 00282 00283 switch ($name) { 00284 case 'nucleusskin': 00285 $this->inData = 1; 00286 break; 00287 case 'meta': 00288 $this->inMeta = 1; 00289 break; 00290 case 'info': 00291 // no action needed 00292 break; 00293 case 'skin': 00294 if (!$this->inMeta) { 00295 $this->inSkin = 1; 00296 $this->currentName = $attrs['name']; 00297 $this->skins[$this->currentName]['type'] = $attrs['type']; 00298 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode']; 00299 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix']; 00300 $this->skins[$this->currentName]['parts'] = array(); 00301 } else { 00302 $this->skins[$attrs['name']] = array(); 00303 $this->skins[$attrs['name']]['parts'] = array(); 00304 } 00305 break; 00306 case 'template': 00307 if (!$this->inMeta) { 00308 $this->inTemplate = 1; 00309 $this->currentName = $attrs['name']; 00310 $this->templates[$this->currentName]['parts'] = array(); 00311 } else { 00312 $this->templates[$attrs['name']] = array(); 00313 $this->templates[$attrs['name']]['parts'] = array(); 00314 } 00315 break; 00316 case 'description': 00317 // no action needed 00318 break; 00319 case 'part': 00320 $this->currentPartName = $attrs['name']; 00321 break; 00322 default: 00323 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name) , '<br />'; 00324 break; 00325 } 00326 00327 // character data never contains other tags 00328 $this->clearCharacterData(); 00329 00330 } 00331 00335 function endElement($parser, $name) { 00336 if ($this->debug) echo 'END: ', htmlspecialchars($name), '<br />'; 00337 00338 switch ($name) { 00339 case 'nucleusskin': 00340 $this->inData = 0; 00341 $this->allRead = 1; 00342 break; 00343 case 'meta': 00344 $this->inMeta = 0; 00345 $this->metaDataRead = 1; 00346 break; 00347 case 'info': 00348 $this->info = $this->getCharacterData(); 00349 case 'skin': 00350 if (!$this->inMeta) $this->inSkin = 0; 00351 break; 00352 case 'template': 00353 if (!$this->inMeta) $this->inTemplate = 0; 00354 break; 00355 case 'description': 00356 if ($this->inSkin) { 00357 $this->skins[$this->currentName]['description'] = $this->getCharacterData(); 00358 } else { 00359 $this->templates[$this->currentName]['description'] = $this->getCharacterData(); 00360 } 00361 break; 00362 case 'part': 00363 if ($this->inSkin) { 00364 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData(); 00365 } else { 00366 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData(); 00367 } 00368 break; 00369 default: 00370 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name), '<br />'; 00371 break; 00372 } 00373 $this->clearCharacterData(); 00374 00375 } 00376 00380 function characterData ($parser, $data) { 00381 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />'; 00382 $this->cdata .= $data; 00383 } 00384 00388 function getCharacterData() { 00389 return $this->cdata; 00390 } 00391 00395 function clearCharacterData() { 00396 $this->cdata = ''; 00397 } 00398 00402 function searchForCandidates($dir) { 00403 $candidates = array(); 00404 00405 $dirhandle = opendir($dir); 00406 while ($filename = readdir($dirhandle)) { 00407 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) { 00408 $xml_file = $dir . $filename . '/skinbackup.xml'; 00409 if (file_exists($xml_file) && is_readable($xml_file)) { 00410 $candidates[$filename] = $filename; //$xml_file; 00411 } 00412 00413 // backwards compatibility 00414 $xml_file = $dir . $filename . '/skindata.xml'; 00415 if (file_exists($xml_file) && is_readable($xml_file)) { 00416 $candidates[$filename] = $filename; //$xml_file; 00417 } 00418 } 00419 } 00420 closedir($dirhandle); 00421 00422 return $candidates; 00423 00424 } 00425 00426 00427 } 00428 00429 00430 class SKINEXPORT { 00431 00432 var $templates; 00433 var $skins; 00434 var $info; 00435 00439 function SKINEXPORT() { 00440 // list of templateIDs to export 00441 $this->templates = array(); 00442 00443 // list of skinIDs to export 00444 $this->skins = array(); 00445 00446 // extra info to be in XML file 00447 $this->info = ''; 00448 } 00449 00457 function addTemplate($id) { 00458 if (!TEMPLATE::existsID($id)) return 0; 00459 00460 $this->templates[$id] = TEMPLATE::getNameFromId($id); 00461 00462 return 1; 00463 } 00464 00472 function addSkin($id) { 00473 if (!SKIN::existsID($id)) return 0; 00474 00475 $this->skins[$id] = SKIN::getNameFromId($id); 00476 00477 return 1; 00478 } 00479 00483 function setInfo($info) { 00484 $this->info = $info; 00485 } 00486 00487 00495 function export($setHeaders = 1) { 00496 if ($setHeaders) { 00497 // make sure the mimetype is correct, and that the data does not show up 00498 // in the browser, but gets saved into and XML file (popup download window) 00499 header('Content-Type: text/xml'); 00500 header('Content-Disposition: attachment; filename="skinbackup.xml"'); 00501 header('Expires: 0'); 00502 header('Pragma: no-cache'); 00503 } 00504 00505 echo "<nucleusskin>\n"; 00506 00507 // meta 00508 echo "\t<meta>\n"; 00509 // skins 00510 foreach ($this->skins as $skinId => $skinName) { 00511 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n"; 00512 } 00513 // templates 00514 foreach ($this->templates as $templateId => $templateName) { 00515 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n"; 00516 } 00517 // extra info 00518 if ($this->info) 00519 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n"; 00520 echo "\t</meta>\n\n\n"; 00521 00522 // contents skins 00523 foreach ($this->skins as $skinId => $skinName) { 00524 $skinId = intval($skinId); 00525 $skinObj = new SKIN($skinId); 00526 00527 echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n"; 00528 00529 echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n"; 00530 00531 $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId); 00532 while ($partObj = mysql_fetch_object($res)) { 00533 echo "\t\t",'<part name="',htmlspecialchars($partObj->stype),'">'; 00534 echo '<![CDATA[', $this->escapeCDATA($partObj->scontent),']]>'; 00535 echo "</part>\n\n"; 00536 } 00537 00538 echo "\t</skin>\n\n\n"; 00539 } 00540 00541 // contents templates 00542 foreach ($this->templates as $templateId => $templateName) { 00543 $templateId = intval($templateId); 00544 00545 echo "\t",'<template name="',htmlspecialchars($templateName),'">',"\n"; 00546 00547 echo "\t\t",'<description>',htmlspecialchars(TEMPLATE::getDesc($templateId)),'</description>',"\n"; 00548 00549 $res = sql_query('SELECT tpartname, tcontent FROM '.sql_table('template').' WHERE tdesc='.$templateId); 00550 while ($partObj = mysql_fetch_object($res)) { 00551 echo "\t\t",'<part name="',htmlspecialchars($partObj->tpartname),'">'; 00552 echo '<![CDATA[', $this->escapeCDATA($partObj->tcontent) ,']]>'; 00553 echo '</part>',"\n\n"; 00554 } 00555 00556 echo "\t</template>\n\n\n"; 00557 } 00558 00559 echo '</nucleusskin>'; 00560 } 00561 00565 function escapeCDATA($cdata) 00566 { 00567 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata); 00568 00569 } 00570 } 00571 00572 ?>