OSDN Git Service

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