OSDN Git Service

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