OSDN Git Service

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