| [ Index ] |
PHP Cross Reference of Nucleus CMS 3.32 |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 4 * Copyright (C) 2002-2007 The Nucleus Group 5 * 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) 11 */ 12 /** 13 * This class contains two classes that can be used for importing and 14 * exporting Nucleus skins: SKINIMPORT and SKINEXPORT 15 * 16 * @license http://nucleuscms.org/license.txt GNU General Public License 17 * @copyright Copyright (C) 2002-2007 The Nucleus Group 18 * @version $Id: skinie.php 1159 2007-05-24 14:37:56Z kaigreve $ 19 */ 20 21 class SKINIMPORT { 22 23 // hardcoded value (see constructor). When 1, interesting info about the 24 // parsing process is sent to the output 25 var $debug; 26 27 // parser/file pointer 28 var $parser; 29 var $fp; 30 31 // which data has been read? 32 var $metaDataRead; 33 var $allRead; 34 35 // extracted data 36 var $skins; 37 var $templates; 38 var $info; 39 40 // to maintain track of where we are inside the XML file 41 var $inXml; 42 var $inData; 43 var $inMeta; 44 var $inSkin; 45 var $inTemplate; 46 var $currentName; 47 var $currentPartName; 48 var $cdata; 49 50 51 52 /** 53 * constructor initializes data structures 54 */ 55 function SKINIMPORT() { 56 // disable magic_quotes_runtime if it's turned on 57 set_magic_quotes_runtime(0); 58 59 // debugging mode? 60 $this->debug = 0; 61 62 $this->reset(); 63 64 } 65 66 function reset() { 67 if ($this->parser) 68 xml_parser_free($this->parser); 69 70 // XML file pointer 71 $this->fp = 0; 72 73 // which data has been read? 74 $this->metaDataRead = 0; 75 $this->allRead = 0; 76 77 // to maintain track of where we are inside the XML file 78 $this->inXml = 0; 79 $this->inData = 0; 80 $this->inMeta = 0; 81 $this->inSkin = 0; 82 $this->inTemplate = 0; 83 $this->currentName = ''; 84 $this->currentPartName = ''; 85 86 // character data pile 87 $this->cdata = ''; 88 89 // list of skinnames and templatenames (will be array of array) 90 $this->skins = array(); 91 $this->templates = array(); 92 93 // extra info included in the XML files (e.g. installation notes) 94 $this->info = ''; 95 96 // init XML parser 97 $this->parser = xml_parser_create(); 98 xml_set_object($this->parser, $this); 99 xml_set_element_handler($this->parser, 'startElement', 'endElement'); 100 xml_set_character_data_handler($this->parser, 'characterData'); 101 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 102 103 } 104 105 /** 106 * Reads an XML file into memory 107 * 108 * @param $filename 109 * Which file to read 110 * @param $metaOnly 111 * Set to 1 when only the metadata needs to be read (optional, default 0) 112 */ 113 function readFile($filename, $metaOnly = 0) { 114 // open file 115 $this->fp = @fopen($filename, 'r'); 116 if (!$this->fp) return 'Failed to open file/URL'; 117 118 // here we go! 119 $this->inXml = 1; 120 121 $tempbuffer = null; 122 123 while (!feof($this->fp)) { 124 $tempbuffer .= fread($this->fp, 4096); 125 } 126 fclose($this->fp); 127 128 /* 129 [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates 130 with CDATA sections. need to investigate consequences. 131 see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA) 132 133 // backwards compatibility with the non-wellformed skinbackup.xml files 134 // generated by v2/v3 (when CDATA sections were present in skins) 135 // split up those CDATA sections into multiple ones 136 $tempbuffer = preg_replace_callback( 137 "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms", 138 create_function( 139 '$matches', 140 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];' 141 ), 142 $tempbuffer 143 ); 144 */ 145 $temp = tmpfile(); 146 fwrite($temp, $tempbuffer); 147 rewind($temp); 148 149 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) { 150 $err = xml_parse( $this->parser, $buffer, feof($temp) ); 151 if (!$err && $this->debug) 152 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />'; 153 } 154 155 // all done 156 $this->inXml = 0; 157 fclose($temp); 158 } 159 160 /** 161 * Returns the list of skin names 162 */ 163 function getSkinNames() { 164 return array_keys($this->skins); 165 } 166 167 /** 168 * Returns the list of template names 169 */ 170 function getTemplateNames() { 171 return array_keys($this->templates); 172 } 173 174 /** 175 * Returns the extra information included in the XML file 176 */ 177 function getInfo() { 178 return $this->info; 179 } 180 181 /** 182 * Writes the skins and templates to the database 183 * 184 * @param $allowOverwrite 185 * set to 1 when allowed to overwrite existing skins with the same name 186 * (default = 0) 187 */ 188 function writeToDatabase($allowOverwrite = 0) { 189 $existingSkins = $this->checkSkinNameClashes(); 190 $existingTemplates = $this->checkTemplateNameClashes(); 191 192 // if not allowed to overwrite, check if any nameclashes exists 193 if (!$allowOverwrite) { 194 if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) 195 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite'; 196 } 197 198 foreach ($this->skins as $skinName => $data) { 199 // 1. if exists: delete all part data, update desc data 200 // if not exists: create desc 201 if (in_array($skinName, $existingSkins)) { 202 $skinObj = SKIN::createFromName($skinName); 203 204 // delete all parts of the skin 205 $skinObj->deleteAllParts(); 206 207 // update general info 208 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']); 209 } else { 210 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']); 211 $skinObj = new SKIN($skinid); 212 } 213 214 // 2. add parts 215 foreach ($data['parts'] as $partName => $partContent) { 216 $skinObj->update($partName, $partContent); 217 } 218 } 219 220 foreach ($this->templates as $templateName => $data) { 221 // 1. if exists: delete all part data, update desc data 222 // if not exists: create desc 223 if (in_array($templateName, $existingTemplates)) { 224 $templateObj = TEMPLATE::createFromName($templateName); 225 226 // delete all parts of the template 227 $templateObj->deleteAllParts(); 228 229 // update general info 230 $templateObj->updateGeneralInfo($templateName, $data['description']); 231 } else { 232 $templateid = TEMPLATE::createNew($templateName, $data['description']); 233 $templateObj = new TEMPLATE($templateid); 234 } 235 236 // 2. add parts 237 foreach ($data['parts'] as $partName => $partContent) { 238 $templateObj->update($partName, $partContent); 239 } 240 } 241 242 243 } 244 245 /** 246 * returns an array of all the skin nameclashes (empty array when no name clashes) 247 */ 248 function checkSkinNameClashes() { 249 $clashes = array(); 250 251 foreach ($this->skins as $skinName => $data) { 252 if (SKIN::exists($skinName)) 253 array_push($clashes, $skinName); 254 } 255 256 return $clashes; 257 } 258 259 /** 260 * returns an array of all the template nameclashes 261 * (empty array when no name clashes) 262 */ 263 function checkTemplateNameClashes() { 264 $clashes = array(); 265 266 foreach ($this->templates as $templateName => $data) { 267 if (TEMPLATE::exists($templateName)) 268 array_push($clashes, $templateName); 269 } 270 271 return $clashes; 272 } 273 274 /** 275 * Called by XML parser for each new start element encountered 276 */ 277 function startElement($parser, $name, $attrs) { 278 foreach($attrs as $key=>$value) $attrs[$key]=htmlspecialchars($value,ENT_QUOTES); 279 280 if ($this->debug) echo 'START: ', htmlspecialchars($name), '<br />'; 281 282 switch ($name) { 283 case 'nucleusskin': 284 $this->inData = 1; 285 break; 286 case 'meta': 287 $this->inMeta = 1; 288 break; 289 case 'info': 290 // no action needed 291 break; 292 case 'skin': 293 if (!$this->inMeta) { 294 $this->inSkin = 1; 295 $this->currentName = $attrs['name']; 296 $this->skins[$this->currentName]['type'] = $attrs['type']; 297 $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode']; 298 $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix']; 299 $this->skins[$this->currentName]['parts'] = array(); 300 } else { 301 $this->skins[$attrs['name']] = array(); 302 $this->skins[$attrs['name']]['parts'] = array(); 303 } 304 break; 305 case 'template': 306 if (!$this->inMeta) { 307 $this->inTemplate = 1; 308 $this->currentName = $attrs['name']; 309 $this->templates[$this->currentName]['parts'] = array(); 310 } else { 311 $this->templates[$attrs['name']] = array(); 312 $this->templates[$attrs['name']]['parts'] = array(); 313 } 314 break; 315 case 'description': 316 // no action needed 317 break; 318 case 'part': 319 $this->currentPartName = $attrs['name']; 320 break; 321 default: 322 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name) , '<br />'; 323 break; 324 } 325 326 // character data never contains other tags 327 $this->clearCharacterData(); 328 329 } 330 331 /** 332 * Called by the XML parser for each closing tag encountered 333 */ 334 function endElement($parser, $name) { 335 if ($this->debug) echo 'END: ', htmlspecialchars($name), '<br />'; 336 337 switch ($name) { 338 case 'nucleusskin': 339 $this->inData = 0; 340 $this->allRead = 1; 341 break; 342 case 'meta': 343 $this->inMeta = 0; 344 $this->metaDataRead = 1; 345 break; 346 case 'info': 347 $this->info = $this->getCharacterData(); 348 case 'skin': 349 if (!$this->inMeta) $this->inSkin = 0; 350 break; 351 case 'template': 352 if (!$this->inMeta) $this->inTemplate = 0; 353 break; 354 case 'description': 355 if ($this->inSkin) { 356 $this->skins[$this->currentName]['description'] = $this->getCharacterData(); 357 } else { 358 $this->templates[$this->currentName]['description'] = $this->getCharacterData(); 359 } 360 break; 361 case 'part': 362 if ($this->inSkin) { 363 $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData(); 364 } else { 365 $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData(); 366 } 367 break; 368 default: 369 echo 'UNEXPECTED TAG: ' , htmlspecialchars($name), '<br />'; 370 break; 371 } 372 $this->clearCharacterData(); 373 374 } 375 376 /** 377 * Called by XML parser for data inside elements 378 */ 379 function characterData ($parser, $data) { 380 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />'; 381 $this->cdata .= $data; 382 } 383 384 /** 385 * Returns the data collected so far 386 */ 387 function getCharacterData() { 388 return $this->cdata; 389 } 390 391 /** 392 * Clears the data buffer 393 */ 394 function clearCharacterData() { 395 $this->cdata = ''; 396 } 397 398 /** 399 * Static method that looks for importable XML files in subdirs of the given dir 400 */ 401 function searchForCandidates($dir) { 402 $candidates = array(); 403 404 $dirhandle = opendir($dir); 405 while ($filename = readdir($dirhandle)) { 406 if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) { 407 $xml_file = $dir . $filename . '/skinbackup.xml'; 408 if (file_exists($xml_file) && is_readable($xml_file)) { 409 $candidates[$filename] = $filename; //$xml_file; 410 } 411 412 // backwards compatibility 413 $xml_file = $dir . $filename . '/skindata.xml'; 414 if (file_exists($xml_file) && is_readable($xml_file)) { 415 $candidates[$filename] = $filename; //$xml_file; 416 } 417 } 418 } 419 closedir($dirhandle); 420 421 return $candidates; 422 423 } 424 425 426 } 427 428 429 class SKINEXPORT { 430 431 var $templates; 432 var $skins; 433 var $info; 434 435 /** 436 * Constructor initializes data structures 437 */ 438 function SKINEXPORT() { 439 // list of templateIDs to export 440 $this->templates = array(); 441 442 // list of skinIDs to export 443 $this->skins = array(); 444 445 // extra info to be in XML file 446 $this->info = ''; 447 } 448 449 /** 450 * Adds a template to be exported 451 * 452 * @param id 453 * template ID 454 * @result false when no such ID exists 455 */ 456 function addTemplate($id) { 457 if (!TEMPLATE::existsID($id)) return 0; 458 459 $this->templates[$id] = TEMPLATE::getNameFromId($id); 460 461 return 1; 462 } 463 464 /** 465 * Adds a skin to be exported 466 * 467 * @param id 468 * skin ID 469 * @result false when no such ID exists 470 */ 471 function addSkin($id) { 472 if (!SKIN::existsID($id)) return 0; 473 474 $this->skins[$id] = SKIN::getNameFromId($id); 475 476 return 1; 477 } 478 479 /** 480 * Sets the extra info to be included in the exported file 481 */ 482 function setInfo($info) { 483 $this->info = $info; 484 } 485 486 487 /** 488 * Outputs the XML contents of the export file 489 * 490 * @param $setHeaders 491 * set to 0 if you don't want to send out headers 492 * (optional, default 1) 493 */ 494 function export($setHeaders = 1) { 495 if ($setHeaders) { 496 // make sure the mimetype is correct, and that the data does not show up 497 // in the browser, but gets saved into and XML file (popup download window) 498 header('Content-Type: text/xml'); 499 header('Content-Disposition: attachment; filename="skinbackup.xml"'); 500 header('Expires: 0'); 501 header('Pragma: no-cache'); 502 } 503 504 echo "<nucleusskin>\n"; 505 506 // meta 507 echo "\t<meta>\n"; 508 // skins 509 foreach ($this->skins as $skinId => $skinName) { 510 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n"; 511 } 512 // templates 513 foreach ($this->templates as $templateId => $templateName) { 514 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n"; 515 } 516 // extra info 517 if ($this->info) 518 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n"; 519 echo "\t</meta>\n\n\n"; 520 521 // contents skins 522 foreach ($this->skins as $skinId => $skinName) { 523 $skinId = intval($skinId); 524 $skinObj = new SKIN($skinId); 525 526 echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n"; 527 528 echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n"; 529 530 $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId); 531 while ($partObj