OSDN Git Service

70f55bd8020a6869b081d56c439a0443d7d26f11
[nucleus-jp/nucleus-jp-ancient.git] / nucleus / libs / skinie.php
1 <?php
2 /*
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4  * Copyright (C) 2002-2012 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  *      This class contains two classes that can be used for importing and
13  *      exporting Nucleus skins: SKINIMPORT and SKINEXPORT
14  */
15
16 class SKINIMPORT {
17
18         // hardcoded value (see constructor). When 1, interesting info about the
19         // parsing process is sent to the output
20         var $debug;
21
22         // parser/file pointer
23         var $parser;
24         var $fp;
25
26         // which data has been read?
27         var $metaDataRead;
28         var $allRead;
29
30         // extracted data
31         var $skins;
32         var $templates;
33         var $info;
34
35         // to maintain track of where we are inside the XML file
36         var $inXml;
37         var $inData;
38         var $inMeta;
39         var $inSkin;
40         var $inTemplate;
41         var $currentName;
42         var $currentPartName;
43         var $cdata;
44
45
46
47         /**
48          * constructor initializes data structures
49          */
50         function SKINIMPORT() {
51                 // disable magic_quotes_runtime if it's turned on
52                 set_magic_quotes_runtime(0);
53
54                 // debugging mode?
55                 $this->debug = 0;
56
57                 $this->reset();
58
59         }
60
61         function reset() {
62                 if ($this->parser)
63                         xml_parser_free($this->parser);
64
65                 // XML file pointer
66                 $this->fp = 0;
67
68                 // which data has been read?
69                 $this->metaDataRead = 0;
70                 $this->allRead = 0;
71
72                 // to maintain track of where we are inside the XML file
73                 $this->inXml = 0;
74                 $this->inData = 0;
75                 $this->inMeta = 0;
76                 $this->inSkin = 0;
77                 $this->inTemplate = 0;
78                 $this->currentName = '';
79                 $this->currentPartName = '';
80
81                 // character data pile
82                 $this->cdata = '';
83
84                 // list of skinnames and templatenames (will be array of array)
85                 $this->skins = array();
86                 $this->templates = array();
87
88                 // extra info included in the XML files (e.g. installation notes)
89                 $this->info = '';
90
91                 // init XML parser
92                 $this->parser = xml_parser_create();
93                 xml_set_object($this->parser, $this);
94                 xml_set_element_handler($this->parser, 'startElement', 'endElement');
95                 xml_set_character_data_handler($this->parser, 'characterData');
96                 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
97
98         }
99
100 /**
101  * Reads an XML file into memory
102  * 
103  * @param $filename
104  *      Which file to read
105  * @param $metaOnly
106  *      Set to 1 when only the metadata needs to be read (optional, default 0)
107  * 
108  * [2004/08/04] Modified by Japanese Package Release Team according to the URL below,
109  *                                      http://japan.nucleuscms.org/bb/viewtopic.php?t=4416
110  * [2004/08/04] Modified by dakarma
111  *                                      Took this out since it messes up good XML if it has skins/templates
112  *                                      with CDATA sections. need to investigate consequences.
113  *                                      see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)
114  */
115         function readFile($filename, $metaOnly = 0) {
116                 // open file
117                 $this->fp = @fopen($filename, 'r');
118                 if (!$this->fp) {
119                         return _SKINIE_ERROR_FAILEDOPEN_FILEURL;
120                 }
121                 
122                 $this->inXml = 1;
123                 $tempbuffer = fread($this->fp,filesize($filename));
124 /*
125                 dakarma wrote
126                 // backwards compatibility with the non-wellformed skinbackup.xml files
127                 // generated by v2/v3 (when CDATA sections were present in skins)
128                 // split up those CDATA sections into multiple ones
129                 $tempbuffer = preg_replace_callback(
130                         "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",
131                         create_function(
132                                 '$matches',
133                                 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'
134                         ),
135                         $tempbuffer
136                 );
137 */
138                 // JP Team wrote
139                 if (function_exists('mb_convert_encoding') && (strtoupper(_CHARSET) != 'ISO-8859-1')) {
140                         mb_detect_order("ASCII, EUC-JP, UTF-8, JIS, SJIS, EUC-CN, ISO-8859-1");
141                         $temp_encode = mb_detect_encoding($tempbuffer);
142                 } else {
143                         $temp_encode = null;
144                 }
145                 rewind($this->fp);
146                 
147                 while ( ($buffer = fgets($this->fp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {
148                         if ($temp_encode) {
149                                 $buffer = mb_convert_encoding($buffer, 'UTF-8', $temp_encode);
150                         }
151                         $err = xml_parse( $this->parser, $buffer, feof($this->fp) );
152                         if (!$err && $this->debug) {
153                                 echo _ERROR . ': ' . xml_error_string(xml_get_error_code($this->parser)) . '<br />';
154                         }
155                 }
156                 
157                 $this->inXml = 0;
158                 fclose($this->fp);
159         }
160
161         /**
162          * Returns the list of skin names
163          */
164         function getSkinNames() {
165                 return array_keys($this->skins);
166         }
167
168         /**
169          * Returns the list of template names
170          */
171         function getTemplateNames() {
172                 return array_keys($this->templates);
173         }
174
175         /**
176          * Returns the extra information included in the XML file
177          */
178         function getInfo() {
179                 return $this->info;
180         }
181
182         /**
183          * Writes the skins and templates to the database
184          *
185          * @param $allowOverwrite
186          *              set to 1 when allowed to overwrite existing skins with the same name
187          *              (default = 0)
188          */
189         function writeToDatabase($allowOverwrite = 0) {
190                 $existingSkins = $this->checkSkinNameClashes();
191                 $existingTemplates = $this->checkTemplateNameClashes();
192
193                 // if not allowed to overwrite, check if any nameclashes exists
194                 if (!$allowOverwrite) {
195                         if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0)) {
196                                 return _SKINIE_NAME_CLASHES_DETECTED;
197                         }
198                 }
199
200                 foreach ($this->skins as $skinName => $data) {
201                         // 1. if exists: delete all part data, update desc data
202                         //    if not exists: create desc
203                         if (in_array($skinName, $existingSkins)) {
204                                 $skinObj = SKIN::createFromName($skinName);
205
206                                 // delete all parts of the skin
207                                 $skinObj->deleteAllParts();
208
209                                 // update general info
210                                 $skinObj->updateGeneralInfo(
211                                         $skinName,
212                                         $data['description'],
213                                         $data['type'],
214                                         $data['includeMode'],
215                                         $data['includePrefix']
216                                 );
217                         } else {
218                                 $skinid = SKIN::createNew(
219                                         $skinName,
220                                         $data['description'],
221                                         $data['type'],
222                                         $data['includeMode'],
223                                         $data['includePrefix']
224                                 );
225                                 $skinObj = new SKIN($skinid);
226                         }
227
228                         // 2. add parts
229                         foreach ($data['parts'] as $partName => $partContent) {
230                                 $skinObj->update($partName, $partContent);
231                         }
232                 }
233
234                 foreach ($this->templates as $templateName => $data) {
235                         // 1. if exists: delete all part data, update desc data
236                         //    if not exists: create desc
237                         if (in_array($templateName, $existingTemplates)) {
238                                 $templateObj = TEMPLATE::createFromName($templateName);
239
240                                 // delete all parts of the template
241                                 $templateObj->deleteAllParts();
242
243                                 // update general info
244                                 $templateObj->updateGeneralInfo($templateName, $data['description']);
245                         } else {
246                                 $templateid = TEMPLATE::createNew($templateName, $data['description']);
247                                 $templateObj = new TEMPLATE($templateid);
248                         }
249
250                         // 2. add parts
251                         foreach ($data['parts'] as $partName => $partContent) {
252                                 $templateObj->update($partName, $partContent);
253                         }
254                 }
255
256
257         }
258
259         /**
260           * returns an array of all the skin nameclashes (empty array when no name clashes)
261           */
262         function checkSkinNameClashes() {
263                 $clashes = array();
264
265                 foreach ($this->skins as $skinName => $data) {
266                         if (SKIN::exists($skinName)) {
267                                 array_push($clashes, $skinName);
268                         }
269                 }
270
271                 return $clashes;
272         }
273
274         /**
275           * returns an array of all the template nameclashes
276           * (empty array when no name clashes)
277           */
278         function checkTemplateNameClashes() {
279                 $clashes = array();
280
281                 foreach ($this->templates as $templateName => $data) {
282                         if (TEMPLATE::exists($templateName)) {
283                                 array_push($clashes, $templateName);
284                         }
285                 }
286
287                 return $clashes;
288         }
289
290         /**
291          * Called by XML parser for each new start element encountered
292          */
293         function startElement($parser, $name, $attrs) {
294                 foreach($attrs as $key=>$value) {
295                         $attrs[$key] = htmlspecialchars($value, ENT_QUOTES);
296                 }
297
298                 if ($this->debug) {
299                         echo 'START: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
300                 }
301
302                 switch ($name) {
303                         case 'nucleusskin':
304                                 $this->inData = 1;
305                                 break;
306                         case 'meta':
307                                 $this->inMeta = 1;
308                                 break;
309                         case 'info':
310                                 // no action needed
311                                 break;
312                         case 'skin':
313                                 if (!$this->inMeta) {
314                                         $this->inSkin = 1;
315                                         $this->currentName = $attrs['name'];
316                                         $this->skins[$this->currentName]['type'] = $attrs['type'];
317                                         $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];
318                                         $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];
319                                         $this->skins[$this->currentName]['parts'] = array();
320                                 } else {
321                                         $this->skins[$attrs['name']] = array();
322                                         $this->skins[$attrs['name']]['parts'] = array();
323                                 }
324                                 break;
325                         case 'template':
326                                 if (!$this->inMeta) {
327                                         $this->inTemplate = 1;
328                                         $this->currentName = $attrs['name'];
329                                         $this->templates[$this->currentName]['parts'] = array();
330                                 } else {
331                                         $this->templates[$attrs['name']] = array();
332                                         $this->templates[$attrs['name']]['parts'] = array();
333                                 }
334                                 break;
335                         case 'description':
336                                 // no action needed
337                                 break;
338                         case 'part':
339                                 $this->currentPartName = $attrs['name'];
340                                 break;
341                         default:
342                                 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
343                                 break;
344                 }
345
346                 // character data never contains other tags
347                 $this->clearCharacterData();
348
349         }
350
351         /**
352           * Called by the XML parser for each closing tag encountered
353           */
354         function endElement($parser, $name) {
355                 if ($this->debug) {
356                         echo 'END: ' . htmlspecialchars($name, ENT_QUOTES) . '<br />';
357                 }
358
359                 switch ($name) {
360                         case 'nucleusskin':
361                                 $this->inData = 0;
362                                 $this->allRead = 1;
363                                 break;
364                         case 'meta':
365                                 $this->inMeta = 0;
366                                 $this->metaDataRead = 1;
367                                 break;
368                         case 'info':
369                                 $this->info = $this->getCharacterData();
370                         case 'skin':
371                                 if (!$this->inMeta) {
372                                         $this->inSkin = 0;
373                                 }
374                                 break;
375                         case 'template':
376                                 if (!$this->inMeta) {
377                                         $this->inTemplate = 0;
378                                 }
379                                 break;
380                         case 'description':
381                                 if ($this->inSkin) {
382                                         $this->skins[$this->currentName]['description'] = $this->getCharacterData();
383                                 } else {
384                                         $this->templates[$this->currentName]['description'] = $this->getCharacterData();
385                                 }
386                                 break;
387                         case 'part':
388                                 if ($this->inSkin) {
389                                         $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
390                                 } else {
391                                         $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();
392                                 }
393                                 break;
394                         default:
395                                 echo _SKINIE_SEELEMENT_UNEXPECTEDTAG . htmlspecialchars($name, ENT_QUOTES) . '<br />';
396                                 break;
397                 }
398                 $this->clearCharacterData();
399
400         }
401
402         /**
403          * Called by XML parser for data inside elements
404          */
405         function characterData ($parser, $data) {
406                 if ($this->debug) {
407                         echo 'NEW DATA: ' . htmlspecialchars($data, ENT_QUOTES) . '<br />';
408                 }
409                 $this->cdata .= $data;
410         }
411
412         /**
413          * Returns the data collected so far
414          */
415         function getCharacterData() {
416 //              echo $this->cdata;
417                 if ( (strtoupper(_CHARSET) == 'UTF-8')
418                         or (strtoupper(_CHARSET) == 'ISO-8859-1')
419                         or (!function_exists('mb_convert_encoding')) ) {
420                         return $this->cdata;
421                 } else {
422                         return mb_convert_encoding($this->cdata, _CHARSET ,'UTF-8');
423                 }
424         }
425
426         /**
427          * Clears the data buffer
428          */
429         function clearCharacterData() {
430                 $this->cdata = '';
431         }
432
433         /**
434          * Static method that looks for importable XML files in subdirs of the given dir
435          */
436         function searchForCandidates($dir) {
437                 $candidates = array();
438
439                 $dirhandle = opendir($dir);
440                 while ($filename = readdir($dirhandle)) {
441                         if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {
442                                 $xml_file = $dir . $filename . '/skinbackup.xml';
443                                 if (file_exists($xml_file) && is_readable($xml_file)) {
444                                         $candidates[$filename] = $filename; //$xml_file;
445                                 }
446
447                                 // backwards compatibility
448                                 $xml_file = $dir . $filename . '/skindata.xml';
449                                 if (file_exists($xml_file) && is_readable($xml_file)) {
450                                         $candidates[$filename] = $filename; //$xml_file;
451                                 }
452                         }
453                 }
454                 closedir($dirhandle);
455
456                 return $candidates;
457
458         }
459
460
461 }
462
463
464 class SKINEXPORT {
465
466         var $templates;
467         var $skins;
468         var $info;
469
470         /**
471          * Constructor initializes data structures
472          */
473         function SKINEXPORT() {
474                 // list of templateIDs to export
475                 $this->templates = array();
476
477                 // list of skinIDs to export
478                 $this->skins = array();
479
480                 // extra info to be in XML file
481                 $this->info = '';
482         }
483
484         /**
485          * Adds a template to be exported
486          *
487          * @param id
488          *              template ID
489          * @result false when no such ID exists
490          */
491         function addTemplate($id) {
492                 if (!TEMPLATE::existsID($id)) {
493                         return 0;
494                 }
495
496
497                 $this->templates[$id] = TEMPLATE::getNameFromId($id);
498
499                 return 1;
500         }
501
502         /**
503          * Adds a skin to be exported
504          *
505          * @param id
506          *              skin ID
507          * @result false when no such ID exists
508          */
509         function addSkin($id) {
510                 if (!SKIN::existsID($id)) {
511                         return 0;
512                 }
513
514                 $this->skins[$id] = SKIN::getNameFromId($id);
515
516                 return 1;
517         }
518
519         /**
520          * Sets the extra info to be included in the exported file
521          */
522         function setInfo($info) {
523                 $this->info = $info;
524         }
525
526
527         /**
528          * Outputs the XML contents of the export file
529          *
530          * @param $setHeaders
531          *              set to 0 if you don't want to send out headers
532          *              (optional, default 1)
533          */
534         function export($setHeaders = 1) {
535                 if ($setHeaders) {
536                         // make sure the mimetype is correct, and that the data does not show up
537                         // in the browser, but gets saved into and XML file (popup download window)
538                         header('Content-Type: text/xml');
539                         header('Content-Disposition: attachment; filename="skinbackup.xml"');
540                         header('Expires: 0');
541                         header('Pragma: no-cache');
542                 }
543
544                 echo "<nucleusskin>\n";
545
546                 // meta
547                 echo "\t<meta>\n";
548                         // skins
549                         foreach ($this->skins as $skinId => $skinName) {
550                                 $skinName = htmlspecialchars($skinName, ENT_QUOTES);
551                                 if (strtoupper(_CHARSET) != 'UTF-8') {
552                                         $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
553                                 }
554                                 echo "\t\t" . '<skin name="' . htmlspecialchars($skinName, ENT_QUOTES) . '" />' . "\n";
555                         }
556                         // templates
557                         foreach ($this->templates as $templateId => $templateName) {
558                                 $templateName = htmlspecialchars($templateName, ENT_QUOTES);
559                                 if (strtoupper(_CHARSET) != 'UTF-8') {
560                                         $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
561                                 }
562                                 echo "\t\t" . '<template name="' . htmlspecialchars($templateName, ENT_QUOTES) . '" />' . "\n";
563                         }
564                         // extra info
565                         if ($this->info) {
566                                 if (strtoupper(_CHARSET) != 'UTF-8') {
567                                         $skin_info = mb_convert_encoding($this->info, 'UTF-8', _CHARSET);
568                                 } else {
569                                         $skin_info = $this->info;
570                                 }
571                                 echo "\t\t<info><![CDATA[" . $skin_info . "]]></info>\n";
572                         }
573                 echo "\t</meta>\n\n\n";
574
575                 // contents skins
576                 foreach ($this->skins as $skinId => $skinName) {
577                         $skinId   = intval($skinId);
578                         $skinObj  = new SKIN($skinId);
579                         $skinName = htmlspecialchars($skinName, ENT_QUOTES);
580                         $contentT = htmlspecialchars($skinObj->getContentType(), ENT_QUOTES);
581                         $incMode  = htmlspecialchars($skinObj->getIncludeMode(), ENT_QUOTES);
582                         $incPrefx = htmlspecialchars($skinObj->getIncludePrefix(), ENT_QUOTES);
583                         $skinDesc = htmlspecialchars($skinObj->getDescription(), ENT_QUOTES);
584                         if (strtoupper(_CHARSET) != 'UTF-8') {
585                                 $skinName = mb_convert_encoding($skinName, 'UTF-8', _CHARSET);
586                                 $contentT = mb_convert_encoding($contentT, 'UTF-8', _CHARSET);
587                                 $incMode  = mb_convert_encoding($incMode,  'UTF-8', _CHARSET);
588                                 $incPrefx = mb_convert_encoding($incPrefx, 'UTF-8', _CHARSET);
589                                 $skinDesc = mb_convert_encoding($skinDesc, 'UTF-8', _CHARSET);
590                         }
591
592                         echo "\t" . '<skin name="' . $skinName . '" type="' . $contentT . '" includeMode="' . $incMode . '" includePrefix="' . $incPrefx . '">' . "\n";
593
594                         echo "\t\t" . '<description>' . $skinDesc . '</description>' . "\n";
595
596                         $que = 'SELECT'
597                                  . '    stype,'
598                                  . '    scontent '
599                                  . 'FROM '
600                                  .      sql_table('skin')
601                                  . ' WHERE'
602                                  . '    sdesc = ' . $skinId;
603                         $res = sql_query($que);
604                         while ($partObj = sql_fetch_object($res)) {
605                                 $type  = htmlspecialchars($partObj->stype, ENT_QUOTES);
606                                 $cdata = $this->escapeCDATA($partObj->scontent);
607                                 if (strtoupper(_CHARSET) != 'UTF-8') {
608                                         $type  = mb_convert_encoding($type,  'UTF-8', _CHARSET);
609                                         $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
610                                 }
611                                 echo "\t\t" . '<part name="' . $type . '">';
612                                 echo '<![CDATA[' . $cdata . ']]>';
613                                 echo "</part>\n\n";
614                         }
615
616                         echo "\t</skin>\n\n\n";
617                 }
618
619                 // contents templates
620                 foreach ($this->templates as $templateId => $templateName) {
621                         $templateId   = intval($templateId);
622                         $templateName = htmlspecialchars($templateName, ENT_QUOTES);
623                         $templateDesc = htmlspecialchars(TEMPLATE::getDesc($templateId), ENT_QUOTES);
624                         if (strtoupper(_CHARSET) != 'UTF-8') {
625                                 $templateName = mb_convert_encoding($templateName, 'UTF-8', _CHARSET);
626                                 $templateDesc = mb_convert_encoding($templateDesc, 'UTF-8', _CHARSET);
627                         }
628
629                         echo "\t" . '<template name="' . $templateName . '">' . "\n";
630
631                         echo "\t\t" . '<description>' . $templateDesc . "</description>\n";
632
633                         $que =  'SELECT'
634                                  .     ' tpartname,'
635                                  .     ' tcontent'
636                                  . ' FROM '
637                                  .     sql_table('template')
638                                  . ' WHERE'
639                                  .     ' tdesc = ' . $templateId;
640                         $res = sql_query($que);
641                         while ($partObj = sql_fetch_object($res)) {
642                                 $type  = htmlspecialchars($partObj->tpartname, ENT_QUOTES);
643                                 $cdata = $this->escapeCDATA($partObj->tcontent);
644                                 if (strtoupper(_CHARSET) != 'UTF-8') {
645                                         $type  = mb_convert_encoding($type,  'UTF-8', _CHARSET);
646                                         $cdata = mb_convert_encoding($cdata, 'UTF-8', _CHARSET);
647                                 }
648                                 echo "\t\t" . '<part name="' . $type . '">';
649                                 echo '<![CDATA[' .  $cdata . ']]>';
650                                 echo '</part>' . "\n\n";
651                         }
652
653                         echo "\t</template>\n\n\n";
654                 }
655
656                 echo '</nucleusskin>';
657         }
658
659         /**
660          * Escapes CDATA content so it can be included in another CDATA section
661          */
662         function escapeCDATA($cdata)
663         {
664                 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);
665
666         }
667 }
668
669 ?>