[ Index ]

PHP Cross Reference of Nucleus CMS 3.32

title

Body

[close]

/nucleus/libs/ -> skinie.php (source)

   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