OSDN Git Service

sync the original code
[nucleus-jp/nucleus-jp-ancient.git] / utf8 / nucleus / libs / skinie.php
1 <?php\r
2 /*\r
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
4  * Copyright (C) 2002-2005 The Nucleus Group\r
5  *\r
6  * This program is free software; you can redistribute it and/or\r
7  * modify it under the terms of the GNU General Public License\r
8  * as published by the Free Software Foundation; either version 2\r
9  * of the License, or (at your option) any later version.\r
10  * (see nucleus/documentation/index.html#license for more info)\r
11  */\r
12 /**\r
13  *      This class contains two classes that can be used for importing and\r
14  *      exporting Nucleus skins: SKINIMPORT and SKINEXPORT\r
15  *\r
16  * @license http://nucleuscms.org/license.txt GNU General Public License\r
17  * @copyright Copyright (C) 2002-2005 The Nucleus Group\r
18  * @version $Id: skinie.php,v 1.4 2005-08-13 07:33:02 kimitake Exp $\r
19  * $NucleusJP: skinie.php,v 1.3 2005/03/12 06:19:05 kimitake Exp $\r
20  */\r
21 \r
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) return 'Failed to open file/URL';\r
118 \r
119                 // here we go!\r
120                 $this->inXml = 1;\r
121 \r
122                 while (!feof($this->fp)) {\r
123                         $tempbuffer .= fread($this->fp, 4096);\r
124                 }\r
125                 fclose($this->fp);\r
126 \r
127 /*\r
128         [2004-08-04] dekarma - Took this out since it messes up good XML if it has skins/templates\r
129                                with CDATA sections. need to investigate consequences.\r
130                                see bug [ 999914 ] Import fails (multiple skins in XML/one of them with CDATA)\r
131         \r
132                 // backwards compatibility with the non-wellformed skinbackup.xml files\r
133                 // generated by v2/v3 (when CDATA sections were present in skins)\r
134                 // split up those CDATA sections into multiple ones\r
135                 $tempbuffer = preg_replace_callback(\r
136                         "/(<!\[CDATA\[[^]]*?<!\[CDATA\[[^]]*)((?:\]\].*?<!\[CDATA.*?)*)(\]\])(.*\]\])/ms",\r
137                         create_function(\r
138                                 '$matches',\r
139                                 'return $matches[1] . preg_replace("/(\]\])(.*?<!\[CDATA)/ms","]]]]><![CDATA[$2",$matches[2])."]]]]><![CDATA[".$matches[4];'\r
140                         ),\r
141                         $tempbuffer\r
142                 );              \r
143 */\r
144                 $temp = tmpfile();\r
145                 fwrite($temp, $tempbuffer);\r
146                 rewind($temp);\r
147 \r
148                 while ( ($buffer = fread($temp, 4096) ) && (!$metaOnly || ($metaOnly && !$this->metaDataRead))) {\r
149                         $err = xml_parse( $this->parser, $buffer, feof($temp) );\r
150                         if (!$err && $this->debug)\r
151                                 echo 'ERROR: ', xml_error_string(xml_get_error_code($this->parser)), '<br />';\r
152                 }\r
153 \r
154                 // all done\r
155                 $this->inXml = 0;\r
156                 fclose($temp);\r
157         }       \r
158         \r
159         /**\r
160          * Returns the list of skin names\r
161          */\r
162         function getSkinNames() {\r
163                 return array_keys($this->skins);\r
164         }\r
165 \r
166         /**\r
167          * Returns the list of template names\r
168          */\r
169         function getTemplateNames() {\r
170                 return array_keys($this->templates);\r
171         }       \r
172         \r
173         /**\r
174          * Returns the extra information included in the XML file\r
175          */\r
176         function getInfo() {\r
177                 return $this->info;\r
178         }\r
179         \r
180         /**\r
181          * Writes the skins and templates to the database \r
182          *\r
183          * @param $allowOverwrite\r
184          *              set to 1 when allowed to overwrite existing skins with the same name\r
185          *              (default = 0)\r
186          */\r
187         function writeToDatabase($allowOverwrite = 0) {\r
188                 $existingSkins = $this->checkSkinNameClashes();\r
189                 $existingTemplates = $this->checkTemplateNameClashes();\r
190                 \r
191                 // if not allowed to overwrite, check if any nameclashes exists\r
192                 if (!$allowOverwrite) {\r
193                         if ((sizeof($existingSkins) > 0) || (sizeof($existingTemplates) > 0))\r
194                                 return 'Name clashes detected, re-run with allowOverwrite = 1 to force overwrite';\r
195                 }\r
196                 \r
197                 foreach ($this->skins as $skinName => $data) {\r
198                         // 1. if exists: delete all part data, update desc data\r
199                         //    if not exists: create desc\r
200                         if (in_array($skinName, $existingSkins)) {\r
201                                 $skinObj = SKIN::createFromName($skinName);\r
202                                 \r
203                                 // delete all parts of the skin\r
204                                 $skinObj->deleteAllParts();\r
205                                 \r
206                                 // update general info\r
207                                 $skinObj->updateGeneralInfo($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);\r
208                         } else {\r
209                                 $skinid = SKIN::createNew($skinName, $data['description'], $data['type'], $data['includeMode'], $data['includePrefix']);\r
210                                 $skinObj = new SKIN($skinid);\r
211                         }\r
212                         \r
213                         // 2. add parts\r
214                         foreach ($data['parts'] as $partName => $partContent) {\r
215                                 $skinObj->update($partName, $partContent);\r
216                         }\r
217                 }\r
218                 \r
219                 foreach ($this->templates as $templateName => $data) {\r
220                         // 1. if exists: delete all part data, update desc data\r
221                         //    if not exists: create desc\r
222                         if (in_array($templateName, $existingTemplates)) {\r
223                                 $templateObj = TEMPLATE::createFromName($templateName);\r
224                                 \r
225                                 // delete all parts of the template\r
226                                 $templateObj->deleteAllParts();\r
227                                 \r
228                                 // update general info\r
229                                 $templateObj->updateGeneralInfo($templateName, $data['description']);\r
230                         } else {\r
231                                 $templateid = TEMPLATE::createNew($templateName, $data['description']);\r
232                                 $templateObj = new TEMPLATE($templateid);\r
233                         }\r
234                         \r
235                         // 2. add parts\r
236                         foreach ($data['parts'] as $partName => $partContent) {\r
237                                 $templateObj->update($partName, $partContent);\r
238                         }                       \r
239                 }\r
240         \r
241                         \r
242         }\r
243         \r
244         /**\r
245           * returns an array of all the skin nameclashes (empty array when no name clashes)\r
246           */\r
247         function checkSkinNameClashes() {\r
248                 $clashes = array();\r
249                 \r
250                 foreach ($this->skins as $skinName => $data) {\r
251                         if (SKIN::exists($skinName))\r
252                                 array_push($clashes, $skinName);\r
253                 }\r
254                 \r
255                 return $clashes;\r
256         }\r
257         \r
258         /**\r
259           * returns an array of all the template nameclashes \r
260           * (empty array when no name clashes)\r
261           */\r
262         function checkTemplateNameClashes() {\r
263                 $clashes = array();\r
264                 \r
265                 foreach ($this->templates as $templateName => $data) {\r
266                         if (TEMPLATE::exists($templateName))\r
267                                 array_push($clashes, $templateName);\r
268                 }\r
269                 \r
270                 return $clashes;\r
271         }\r
272                 \r
273         /**\r
274          * Called by XML parser for each new start element encountered\r
275          */\r
276         function startElement($parser, $name, $attrs) {\r
277                 if ($this->debug) echo 'START: ', $name, '<br />';\r
278                 \r
279                 switch ($name) {\r
280                         case 'nucleusskin':\r
281                                 $this->inData = 1;\r
282                                 break;\r
283                         case 'meta':\r
284                                 $this->inMeta = 1;\r
285                                 break;\r
286                         case 'info':\r
287                                 // no action needed\r
288                                 break;\r
289                         case 'skin':\r
290                                 if (!$this->inMeta) {\r
291                                         $this->inSkin = 1;\r
292                                         $this->currentName = $attrs['name'];\r
293                                         $this->skins[$this->currentName]['type'] = $attrs['type'];\r
294                                         $this->skins[$this->currentName]['includeMode'] = $attrs['includeMode'];\r
295                                         $this->skins[$this->currentName]['includePrefix'] = $attrs['includePrefix'];                                    \r
296                                         $this->skins[$this->currentName]['parts'] = array();                                                                            \r
297                                 } else {\r
298                                         $this->skins[$attrs['name']] = array();                         \r
299                                         $this->skins[$attrs['name']]['parts'] = array();                                                                        \r
300                                 }\r
301                                 break;\r
302                         case 'template':\r
303                                 if (!$this->inMeta) {\r
304                                         $this->inTemplate = 1;\r
305                                         $this->currentName = $attrs['name'];\r
306                                         $this->templates[$this->currentName]['parts'] = array();                                                                                                                        \r
307                                 } else {\r
308                                         $this->templates[$attrs['name']] = array();                             \r
309                                         $this->templates[$attrs['name']]['parts'] = array();                                                                    \r
310                                 }\r
311                                 break;\r
312                         case 'description':\r
313                                 // no action needed\r
314                                 break;\r
315                         case 'part':\r
316                                 $this->currentPartName = $attrs['name'];\r
317                                 break;\r
318                         default:\r
319                                 echo 'UNEXPECTED TAG: ' , $name , '<br />';\r
320                                 break;\r
321                 }\r
322                 \r
323                 // character data never contains other tags\r
324                 $this->clearCharacterData(); \r
325                 \r
326         }\r
327 \r
328         /**\r
329           * Called by the XML parser for each closing tag encountered\r
330           */\r
331         function endElement($parser, $name) {\r
332                 if ($this->debug) echo 'END: ', $name, '<br />';\r
333                 \r
334                 switch ($name) {\r
335                         case 'nucleusskin':\r
336                                 $this->inData = 0;\r
337                                 $this->allRead = 1;\r
338                                 break;\r
339                         case 'meta':\r
340                                 $this->inMeta = 0;\r
341                                 $this->metaDataRead = 1;\r
342                                 break;\r
343                         case 'info':\r
344                                 $this->info = $this->getCharacterData();\r
345                         case 'skin':\r
346                                 if (!$this->inMeta) $this->inSkin = 0;\r
347                                 break;\r
348                         case 'template':\r
349                                 if (!$this->inMeta) $this->inTemplate = 0;                      \r
350                                 break;\r
351                         case 'description':\r
352                                 if ($this->inSkin) {\r
353                                         $this->skins[$this->currentName]['description'] = $this->getCharacterData();\r
354                                 } else {\r
355                                         $this->templates[$this->currentName]['description'] = $this->getCharacterData();                                \r
356                                 }\r
357                                 break;\r
358                         case 'part':\r
359                                 if ($this->inSkin) {\r
360                                         $this->skins[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();\r
361                                 } else {\r
362                                         $this->templates[$this->currentName]['parts'][$this->currentPartName] = $this->getCharacterData();                              \r
363                                 }\r
364                                 break;\r
365                         default:\r
366                                 echo 'UNEXPECTED TAG: ' , $name, '<br />';\r
367                                 break;\r
368                 }\r
369                 $this->clearCharacterData();\r
370 \r
371         }\r
372         \r
373         /**\r
374          * Called by XML parser for data inside elements\r
375          */\r
376         function characterData ($parser, $data) {\r
377                 if ($this->debug) echo 'NEW DATA: ', htmlspecialchars($data), '<br />';\r
378                 $this->cdata .= $data;\r
379         }\r
380         \r
381         /**\r
382          * Returns the data collected so far\r
383          */\r
384         function getCharacterData() {\r
385                 return $this->cdata;\r
386         }\r
387         \r
388         /**\r
389          * Clears the data buffer\r
390          */\r
391         function clearCharacterData() {\r
392                 $this->cdata = '';\r
393         }\r
394         \r
395         /**\r
396          * Static method that looks for importable XML files in subdirs of the given dir\r
397          */\r
398         function searchForCandidates($dir) {\r
399                 $candidates = array();\r
400 \r
401                 $dirhandle = opendir($dir);\r
402                 while ($filename = readdir($dirhandle)) {\r
403                         if (@is_dir($dir . $filename) && ($filename != '.') && ($filename != '..')) {\r
404                                 $xml_file = $dir . $filename . '/skinbackup.xml';\r
405                                 if (file_exists($xml_file) && is_readable($xml_file)) {\r
406                                         $candidates[$filename] = $filename; //$xml_file;\r
407                                 } \r
408 \r
409                                 // backwards compatibility                      \r
410                                 $xml_file = $dir . $filename . '/skindata.xml';\r
411                                 if (file_exists($xml_file) && is_readable($xml_file)) {\r
412                                         $candidates[$filename] = $filename; //$xml_file;\r
413                                 } \r
414                         }\r
415                 }\r
416                 closedir($dirhandle);\r
417                 \r
418                 return $candidates;\r
419         \r
420         }\r
421          \r
422         \r
423 }\r
424 \r
425 \r
426 class SKINEXPORT {\r
427 \r
428         var $templates;\r
429         var $skins;\r
430         var $info;\r
431         \r
432         /**\r
433          * Constructor initializes data structures\r
434          */\r
435         function SKINEXPORT() {\r
436                 // list of templateIDs to export\r
437                 $this->templates = array();\r
438                 \r
439                 // list of skinIDs to export\r
440                 $this->skins = array();\r
441                 \r
442                 // extra info to be in XML file\r
443                 $this->info = '';\r
444         }\r
445         \r
446         /**\r
447          * Adds a template to be exported\r
448          *\r
449          * @param id\r
450          *              template ID\r
451          * @result false when no such ID exists\r
452          */\r
453         function addTemplate($id) {\r
454                 if (!TEMPLATE::existsID($id)) return 0;\r
455         \r
456                 $this->templates[$id] = TEMPLATE::getNameFromId($id);\r
457                 \r
458                 return 1;\r
459         }\r
460         \r
461         /**\r
462          * Adds a skin to be exported\r
463          *\r
464          * @param id \r
465          *              skin ID\r
466          * @result false when no such ID exists\r
467          */     \r
468         function addSkin($id) {\r
469                 if (!SKIN::existsID($id)) return 0;\r
470                 \r
471                 $this->skins[$id] = SKIN::getNameFromId($id);\r
472                 \r
473                 return 1;\r
474         }\r
475         \r
476         /**\r
477          * Sets the extra info to be included in the exported file\r
478          */\r
479         function setInfo($info) {\r
480                 $this->info = $info;\r
481         }\r
482         \r
483         \r
484         /**\r
485          * Outputs the XML contents of the export file\r
486          *\r
487          * @param $setHeaders\r
488          *              set to 0 if you don't want to send out headers\r
489          *              (optional, default 1)\r
490          */\r
491         function export($setHeaders = 1) {\r
492                 if ($setHeaders) {\r
493                         // make sure the mimetype is correct, and that the data does not show up \r
494                         // in the browser, but gets saved into and XML file (popup download window)\r
495                         header('Content-Type: text/xml');\r
496                         header('Content-Disposition: attachment; filename="skinbackup.xml"');\r
497                         header('Expires: 0');\r
498                         header('Pragma: no-cache');\r
499                 }\r
500         \r
501                 echo "<nucleusskin>\n";\r
502         \r
503                 // meta\r
504                 echo "\t<meta>\n";\r
505                         // skins\r
506                         foreach ($this->skins as $skinId => $skinName) {\r
507                                 echo "\t\t", '<skin name="',htmlspecialchars($skinName),'" />',"\n";\r
508                         }\r
509                         // templates\r
510                         foreach ($this->templates as $templateId => $templateName) {\r
511                                 echo "\t\t", '<template name="',htmlspecialchars($templateName),'" />',"\n";\r
512                         }\r
513                         // extra info\r
514                         if ($this->info)\r
515                                 echo "\t\t<info><![CDATA[",$this->info,"]]></info>\n";\r
516                 echo "\t</meta>\n\n\n";\r
517                 \r
518                 // contents skins\r
519                 foreach ($this->skins as $skinId => $skinName) {\r
520                         $skinId = intval($skinId);\r
521                         $skinObj = new SKIN($skinId);\r
522                         \r
523                         echo "\t", '<skin name="',htmlspecialchars($skinName),'" type="',htmlspecialchars($skinObj->getContentType()),'" includeMode="',htmlspecialchars($skinObj->getIncludeMode()),'" includePrefix="',htmlspecialchars($skinObj->getIncludePrefix()),'">',"\n";\r
524                         \r
525                         echo "\t\t", '<description>',htmlspecialchars($skinObj->getDescription()),'</description>',"\n";\r
526                         \r
527                         $res = sql_query('SELECT stype, scontent FROM '.sql_table('skin').' WHERE sdesc='.$skinId);\r
528                         while ($partObj = mysql_fetch_object($res)) {\r
529                                 echo "\t\t",'<part name="',htmlspecialchars($partObj->stype),'">';\r
530                                 echo '<![CDATA[', $this->escapeCDATA($partObj->scontent),']]>';\r
531                                 echo "</part>\n\n";\r
532                         }\r
533                         \r
534                         echo "\t</skin>\n\n\n";\r
535                 }\r
536                 \r
537                 // contents templates\r
538                 foreach ($this->templates as $templateId => $templateName) {\r
539                         $templateId = intval($templateId);\r
540                         \r
541                         echo "\t",'<template name="',htmlspecialchars($templateName),'">',"\n";\r
542                         \r
543                         echo "\t\t",'<description>',htmlspecialchars(TEMPLATE::getDesc($templateId)),'</description>',"\n";                     \r
544                         \r
545                         $res = sql_query('SELECT tpartname, tcontent FROM '.sql_table('template').' WHERE tdesc='.$templateId);\r
546                         while ($partObj = mysql_fetch_object($res)) {\r
547                                 echo "\t\t",'<part name="',htmlspecialchars($partObj->tpartname),'">';\r
548                                 echo '<![CDATA[', $this->escapeCDATA($partObj->tcontent) ,']]>';\r
549                                 echo '</part>',"\n\n";\r
550                         }\r
551                         \r
552                         echo "\t</template>\n\n\n";\r
553                 }               \r
554                 \r
555                 echo '</nucleusskin>';\r
556         }\r
557         \r
558         /**\r
559          * Escapes CDATA content so it can be included in another CDATA section\r
560          */\r
561         function escapeCDATA($cdata)\r
562         {\r
563                 return preg_replace('/]]>/', ']]]]><![CDATA[>', $cdata);\r
564                 \r
565         }\r
566 }\r
567 \r
568 ?>\r