OSDN Git Service

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