OSDN Git Service

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