Dotclear

source: inc/public/class.dc.template.php @ 2610:a9bc70b5a85d

Revision 2610:a9bc70b5a85d, 89.4 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

Merge 2.6.2 commits into default branch

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14class dcTemplate extends template
15{
16     private $core;
17     private $current_tag;
18
19     protected $unknown_value_handler = null;
20     protected $unknown_block_handler = null;
21
22     function __construct($cache_dir,$self_name,$core)
23     {
24          parent::__construct($cache_dir,$self_name);
25
26          $this->remove_php = !$core->blog->settings->system->tpl_allow_php;
27          $this->use_cache = $core->blog->settings->system->tpl_use_cache;
28
29          $this->tag_block = '<tpl:(\w+)(?:(\s+.*?)>|>)((?:[^<]|<(?!/?tpl:\1)|(?R))*)</tpl:\1>';
30          $this->tag_value = '{{tpl:(\w+)(\s(.*?))?}}';
31
32          $this->core =& $core;
33
34          # Transitional tags
35          $this->addValue('EntryTrackbackCount',array($this,'EntryPingCount'));
36          $this->addValue('EntryTrackbackData',array($this,'EntryPingData'));
37          $this->addValue('EntryTrackbackLink',array($this,'EntryPingLink'));
38
39          # l10n
40          $this->addValue('lang',array($this,'l10n'));
41
42          # Loops test tags
43          $this->addBlock('LoopPosition',array($this,'LoopPosition'));
44          $this->addValue('LoopIndex',array($this,'LoopIndex'));
45
46          # Archives
47          $this->addBlock('Archives',array($this,'Archives'));
48          $this->addBlock('ArchivesHeader',array($this,'ArchivesHeader'));
49          $this->addBlock('ArchivesFooter',array($this,'ArchivesFooter'));
50          $this->addBlock('ArchivesYearHeader',array($this,'ArchivesYearHeader'));
51          $this->addBlock('ArchivesYearFooter',array($this,'ArchivesYearFooter'));
52          $this->addValue('ArchiveDate',array($this,'ArchiveDate'));
53          $this->addBlock('ArchiveNext',array($this,'ArchiveNext'));
54          $this->addBlock('ArchivePrevious',array($this,'ArchivePrevious'));
55          $this->addValue('ArchiveEntriesCount',array($this,'ArchiveEntriesCount'));
56          $this->addValue('ArchiveURL',array($this,'ArchiveURL'));
57
58          # Blog
59          $this->addValue('BlogArchiveURL',array($this,'BlogArchiveURL'));
60          $this->addValue('BlogCopyrightNotice',array($this,'BlogCopyrightNotice'));
61          $this->addValue('BlogDescription',array($this,'BlogDescription'));
62          $this->addValue('BlogEditor',array($this,'BlogEditor'));
63          $this->addValue('BlogFeedID',array($this,'BlogFeedID'));
64          $this->addValue('BlogFeedURL',array($this,'BlogFeedURL'));
65          $this->addValue('BlogRSDURL',array($this,'BlogRSDURL'));
66          $this->addValue('BlogName',array($this,'BlogName'));
67          $this->addValue('BlogLanguage',array($this,'BlogLanguage'));
68          $this->addValue('BlogThemeURL',array($this,'BlogThemeURL'));
69        $this->addValue('BlogParentThemeURL',array($this,'BlogParentThemeURL'));
70          $this->addValue('BlogUpdateDate',array($this,'BlogUpdateDate'));
71          $this->addValue('BlogID',array($this,'BlogID'));
72          $this->addValue('BlogURL',array($this,'BlogURL'));
73          $this->addValue('BlogXMLRPCURL',array($this,'BlogXMLRPCURL'));
74          $this->addValue('BlogPublicURL',array($this,'BlogPublicURL'));
75          $this->addValue('BlogQmarkURL',array($this,'BlogQmarkURL'));
76          $this->addValue('BlogMetaRobots',array($this,'BlogMetaRobots'));
77
78          # Categories
79          $this->addBlock('Categories',array($this,'Categories'));
80          $this->addBlock('CategoriesHeader',array($this,'CategoriesHeader'));
81          $this->addBlock('CategoriesFooter',array($this,'CategoriesFooter'));
82          $this->addBlock('CategoryIf',array($this,'CategoryIf'));
83          $this->addBlock('CategoryFirstChildren',array($this,'CategoryFirstChildren'));
84          $this->addBlock('CategoryParents',array($this,'CategoryParents'));
85          $this->addValue('CategoryFeedURL',array($this,'CategoryFeedURL'));
86          $this->addValue('CategoryURL',array($this,'CategoryURL'));
87          $this->addValue('CategoryShortURL',array($this,'CategoryShortURL'));
88          $this->addValue('CategoryDescription',array($this,'CategoryDescription'));
89          $this->addValue('CategoryTitle',array($this,'CategoryTitle'));
90          $this->addValue('CategoryEntriesCount',array($this,'CategoryEntriesCount'));
91
92          # Comments
93          $this->addBlock('Comments',array($this,'Comments'));
94          $this->addValue('CommentAuthor',array($this,'CommentAuthor'));
95          $this->addValue('CommentAuthorDomain',array($this,'CommentAuthorDomain'));
96          $this->addValue('CommentAuthorLink',array($this,'CommentAuthorLink'));
97          $this->addValue('CommentAuthorMailMD5',array($this,'CommentAuthorMailMD5'));
98          $this->addValue('CommentAuthorURL',array($this,'CommentAuthorURL'));
99          $this->addValue('CommentContent',array($this,'CommentContent'));
100          $this->addValue('CommentDate',array($this,'CommentDate'));
101          $this->addValue('CommentTime',array($this,'CommentTime'));
102          $this->addValue('CommentEmail',array($this,'CommentEmail'));
103          $this->addValue('CommentEntryTitle',array($this,'CommentEntryTitle'));
104          $this->addValue('CommentFeedID',array($this,'CommentFeedID'));
105          $this->addValue('CommentID',array($this,'CommentID'));
106          $this->addBlock('CommentIf',array($this,'CommentIf'));
107          $this->addValue('CommentIfFirst',array($this,'CommentIfFirst'));
108          $this->addValue('CommentIfMe',array($this,'CommentIfMe'));
109          $this->addValue('CommentIfOdd',array($this,'CommentIfOdd'));
110          $this->addValue('CommentIP',array($this,'CommentIP'));
111          $this->addValue('CommentOrderNumber',array($this,'CommentOrderNumber'));
112          $this->addBlock('CommentsFooter',array($this,'CommentsFooter'));
113          $this->addBlock('CommentsHeader',array($this,'CommentsHeader'));
114          $this->addValue('CommentPostURL',array($this,'CommentPostURL'));
115          $this->addBlock('IfCommentAuthorEmail',array($this,'IfCommentAuthorEmail'));
116          $this->addValue('CommentHelp',array($this,'CommentHelp'));
117
118          # Comment preview
119          $this->addBlock('IfCommentPreview',array($this,'IfCommentPreview'));
120          $this->addValue('CommentPreviewName',array($this,'CommentPreviewName'));
121          $this->addValue('CommentPreviewEmail',array($this,'CommentPreviewEmail'));
122          $this->addValue('CommentPreviewSite',array($this,'CommentPreviewSite'));
123          $this->addValue('CommentPreviewContent',array($this,'CommentPreviewContent'));
124          $this->addValue('CommentPreviewCheckRemember',array($this,'CommentPreviewCheckRemember'));
125
126          # Entries
127          $this->addBlock('DateFooter',array($this,'DateFooter'));
128          $this->addBlock('DateHeader',array($this,'DateHeader'));
129          $this->addBlock('Entries',array($this,'Entries'));
130          $this->addBlock('EntriesFooter',array($this,'EntriesFooter'));
131          $this->addBlock('EntriesHeader',array($this,'EntriesHeader'));
132          $this->addValue('EntryExcerpt',array($this,'EntryExcerpt'));
133          $this->addValue('EntryAuthorCommonName',array($this,'EntryAuthorCommonName'));
134          $this->addValue('EntryAuthorDisplayName',array($this,'EntryAuthorDisplayName'));
135          $this->addValue('EntryAuthorEmail',array($this,'EntryAuthorEmail'));
136          $this->addValue('EntryAuthorID',array($this,'EntryAuthorID'));
137          $this->addValue('EntryAuthorLink',array($this,'EntryAuthorLink'));
138          $this->addValue('EntryAuthorURL',array($this,'EntryAuthorURL'));
139          $this->addValue('EntryBasename',array($this,'EntryBasename'));
140          $this->addValue('EntryCategory',array($this,'EntryCategory'));
141          $this->addBlock('EntryCategoriesBreadcrumb',array($this,'EntryCategoriesBreadcrumb'));
142          $this->addValue('EntryCategoryID',array($this,'EntryCategoryID'));
143          $this->addValue('EntryCategoryURL',array($this,'EntryCategoryURL'));
144          $this->addValue('EntryCategoryShortURL',array($this,'EntryCategoryShortURL'));
145          $this->addValue('EntryCommentCount',array($this,'EntryCommentCount'));
146          $this->addValue('EntryContent',array($this,'EntryContent'));
147          $this->addValue('EntryDate',array($this,'EntryDate'));
148          $this->addValue('EntryFeedID',array($this,'EntryFeedID'));
149          $this->addValue('EntryFirstImage',array($this,'EntryFirstImage'));
150          $this->addValue('EntryID',array($this,'EntryID'));
151          $this->addBlock('EntryIf',array($this,'EntryIf'));
152          $this->addValue('EntryIfFirst',array($this,'EntryIfFirst'));
153          $this->addValue('EntryIfOdd',array($this,'EntryIfOdd'));
154          $this->addValue('EntryIfSelected',array($this,'EntryIfSelected'));
155          $this->addValue('EntryLang',array($this,'EntryLang'));
156          $this->addBlock('EntryNext',array($this,'EntryNext'));
157          $this->addValue('EntryPingCount',array($this,'EntryPingCount'));
158          $this->addValue('EntryPingData',array($this,'EntryPingData'));
159          $this->addValue('EntryPingLink',array($this,'EntryPingLink'));
160          $this->addBlock('EntryPrevious',array($this,'EntryPrevious'));
161          $this->addValue('EntryTitle',array($this,'EntryTitle'));
162          $this->addValue('EntryTime',array($this,'EntryTime'));
163          $this->addValue('EntryURL',array($this,'EntryURL'));
164
165          # Languages
166          $this->addBlock('Languages',array($this,'Languages'));
167          $this->addBlock('LanguagesHeader',array($this,'LanguagesHeader'));
168          $this->addBlock('LanguagesFooter',array($this,'LanguagesFooter'));
169          $this->addValue('LanguageCode',array($this,'LanguageCode'));
170          $this->addBlock('LanguageIfCurrent',array($this,'LanguageIfCurrent'));
171          $this->addValue('LanguageURL',array($this,'LanguageURL'));
172
173          # Pagination
174          $this->addBlock('Pagination',array($this,'Pagination'));
175          $this->addValue('PaginationCounter',array($this,'PaginationCounter'));
176          $this->addValue('PaginationCurrent',array($this,'PaginationCurrent'));
177          $this->addBlock('PaginationIf',array($this,'PaginationIf'));
178          $this->addValue('PaginationURL',array($this,'PaginationURL'));
179
180          # Trackbacks
181          $this->addValue('PingBlogName',array($this,'PingBlogName'));
182          $this->addValue('PingContent',array($this,'PingContent'));
183          $this->addValue('PingDate',array($this,'PingDate'));
184          $this->addValue('PingEntryTitle',array($this,'PingEntryTitle'));
185          $this->addValue('PingFeedID',array($this,'PingFeedID'));
186          $this->addValue('PingID',array($this,'PingID'));
187          $this->addValue('PingIfFirst',array($this,'PingIfFirst'));
188          $this->addValue('PingIfOdd',array($this,'PingIfOdd'));
189          $this->addValue('PingIP',array($this,'PingIP'));
190          $this->addValue('PingNoFollow',array($this,'PingNoFollow'));
191          $this->addValue('PingOrderNumber',array($this,'PingOrderNumber'));
192          $this->addValue('PingPostURL',array($this,'PingPostURL'));
193          $this->addBlock('Pings',array($this,'Pings'));
194          $this->addBlock('PingsFooter',array($this,'PingsFooter'));
195          $this->addBlock('PingsHeader',array($this,'PingsHeader'));
196          $this->addValue('PingTime',array($this,'PingTime'));
197          $this->addValue('PingTitle',array($this,'PingTitle'));
198          $this->addValue('PingAuthorURL',array($this,'PingAuthorURL'));
199
200          # System
201          $this->addValue('SysBehavior',array($this,'SysBehavior'));
202          $this->addBlock('SysIf',array($this,'SysIf'));
203          $this->addBlock('SysIfCommentPublished',array($this,'SysIfCommentPublished'));
204          $this->addBlock('SysIfCommentPending',array($this,'SysIfCommentPending'));
205          $this->addBlock('SysIfFormError',array($this,'SysIfFormError'));
206          $this->addValue('SysFeedSubtitle',array($this,'SysFeedSubtitle'));
207          $this->addValue('SysFormError',array($this,'SysFormError'));
208          $this->addValue('SysPoweredBy',array($this,'SysPoweredBy'));
209          $this->addValue('SysSearchString',array($this,'SysSearchString'));
210          $this->addValue('SysSelfURI',array($this,'SysSelfURI'));
211
212          # Generic
213          $this->addValue('else',array($this,'GenericElse'));
214     }
215
216     public function getData($________)
217     {
218          # --BEHAVIOR-- tplBeforeData
219          if ($this->core->hasBehavior('tplBeforeData'))
220          {
221               self::$_r = $this->core->callBehavior('tplBeforeData',$this->core);
222               if (self::$_r) {
223                    return self::$_r;
224               }
225          }
226
227          parent::getData($________);
228
229          # --BEHAVIOR-- tplAfterData
230          if ($this->core->hasBehavior('tplAfterData')) {
231               $this->core->callBehavior('tplAfterData',$this->core,self::$_r);
232          }
233
234          return self::$_r;
235     }
236
237     public function compileBlockNode($tag,$attr,$content)
238     {
239          $this->current_tag = $tag;
240          $attr = new ArrayObject($attr);
241          # --BEHAVIOR-- templateBeforeBlock
242          $res = $this->core->callBehavior('templateBeforeBlock',$this->core,$this->current_tag,$attr);
243
244          # --BEHAVIOR-- templateInsideBlock
245          $this->core->callBehavior('templateInsideBlock',$this->core,$this->current_tag,$attr,array(&$content));
246
247          $res .= parent::compileBlockNode($this->current_tag,$attr,$content);
248
249          # --BEHAVIOR-- templateAfterBlock
250          $res .= $this->core->callBehavior('templateAfterBlock',$this->core,$this->current_tag,$attr);
251
252          return $res;
253     }
254
255     public function compileValueNode($tag,$attr,$str_attr)
256     {
257          $this->current_tag = $tag;
258
259          $attr = new ArrayObject($attr);
260          # --BEHAVIOR-- templateBeforeValue
261          $res = $this->core->callBehavior('templateBeforeValue',$this->core,$this->current_tag,$attr);
262
263          $res .= parent::compileValueNode($this->current_tag,$attr,$str_attr);
264
265          # --BEHAVIOR-- templateAfterValue
266          $res .= $this->core->callBehavior('templateAfterValue',$this->core,$this->current_tag,$attr);
267
268          return $res;
269     }
270
271     public function getFilters($attr)
272     {
273          $p[0] = '0';   # encode_xml
274          $p[1] = '0';   # remove_html
275          $p[2] = '0';   # cut_string
276          $p[3] = '0';   # lower_case
277          $p[4] = '0';   # upper_case or capitalize
278
279          $p[0] = (integer) (!empty($attr['encode_xml']) || !empty($attr['encode_html']));
280          $p[1] = (integer) !empty($attr['remove_html']);
281
282          if (!empty($attr['cut_string']) && (integer) $attr['cut_string'] > 0) {
283               $p[2] = (integer) $attr['cut_string'];
284          }
285
286          $p[3] = (integer) !empty($attr['lower_case']);
287          $p[4] = (integer) !empty($attr['upper_case']);
288          $p[4] = (!empty($attr['capitalize']) ? 2 : $p[4]);
289
290          return "context::global_filter(%s,".implode(",",$p).",'".addslashes($this->current_tag)."')";
291     }
292
293     public static function getOperator($op)
294     {
295          switch (strtolower($op))
296          {
297               case 'or':
298               case '||':
299                    return '||';
300               case 'and':
301               case '&&':
302               default:
303                    return '&&';
304          }
305     }
306
307     public function getSortByStr($attr,$table = null)
308     {
309          $res = array();
310
311          $default_order = 'desc';
312
313          $default_alias = array(
314               'post' => array(
315                    'title' => 'post_title',
316                    'selected' => 'post_selected',
317                    'author' => 'user_id',
318                    'date' => 'post_dt',
319                    'id' => 'post_id',
320                    'comment' => 'nb_comment',
321                    'trackback' => 'nb_trackback'
322               ),
323               'comment' => array(
324                    'author' => 'comment_author',
325                    'date' => 'comment_dt',
326                    'id' => 'comment_id'
327               )
328          );
329
330          $alias = new ArrayObject();
331
332          # --BEHAVIOR-- templateCustomSortByAlias
333          $this->core->callBehavior('templateCustomSortByAlias',$alias);
334
335          $alias = $alias->getArrayCopy();
336
337          if (is_array($alias)) {
338               foreach ($alias as $k => $v) {
339                    if (!is_array($v)) {
340                         $alias[$k] = array();
341                    }
342                    if (!is_array($v)) {
343                         $default_alias[$k] = array();
344                    }
345                    $default_alias[$k] = array_merge($default_alias[$k],$alias[$k]);
346               }
347          }
348
349          if (!array_key_exists($table,$default_alias)) {
350               return implode(', ',$res);
351          }
352
353          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
354               $default_order = $attr['order'];
355          }
356          if (isset($attr['sortby'])) {
357               $sorts = explode(',',$attr['sortby']);
358               foreach ($sorts as $k => $sort) {
359                    $order = $default_order;
360                    if (preg_match('/([a-z]*)\s*\?(desc|asc)$/i',$sort,$matches)) {
361                         $sort = $matches[1];
362                         $order = $matches[2];
363                    }
364                    if (array_key_exists($sort,$default_alias[$table])) {
365                         array_push($res,$default_alias[$table][$sort].' '.$order);
366                    }
367               }
368          }
369
370          if (count($res) === 0) {
371               array_push($res,$default_alias[$table]['date'].' '.$default_order);
372          }
373
374          return implode(', ',$res);
375     }
376
377     public static function getAge($attr)
378     {
379          if (isset($attr['age']) && preg_match('/^(\-[0-9]+|last).*$/i',$attr['age'])) {
380               if (($ts = strtotime($attr['age'])) !== false) {
381                    return dt::str('%Y-%m-%d %H:%m:%S',$ts);
382               }
383          }
384          return '';
385     }
386
387     /* TEMPLATE FUNCTIONS
388     ------------------------------------------------------- */
389
390     public function l10n($attr,$str_attr)
391     {
392          # Normalize content
393          $str_attr = preg_replace('/\s+/x',' ',$str_attr);
394
395          return "<?php echo __('".str_replace("'","\\'",$str_attr)."'); ?>";
396     }
397
398     public function LoopPosition($attr,$content)
399     {
400          $start = isset($attr['start']) ? (integer) $attr['start'] : '0';
401          $length = isset($attr['length']) ? (integer) $attr['length'] : 'null';
402          $even = isset($attr['even']) ? (integer) (boolean) $attr['even'] : 'null';
403
404          if ($start > 0) {
405               $start--;
406          }
407
408          return
409          '<?php if ($_ctx->loopPosition('.$start.','.$length.','.$even.')) : ?>'.
410          $content.
411          "<?php endif; ?>";
412     }
413
414     public function LoopIndex($attr)
415     {
416          $f = $this->getFilters($attr);
417          return '<?php echo '.sprintf($f,'(!$_ctx->cur_loop ? 0 : $_ctx->cur_loop->index() + 1)').'; ?>';
418     }
419
420
421     /* Archives ------------------------------------------- */
422     /*dtd
423     <!ELEMENT tpl:Archives - - -- Archives dates loop -->
424     <!ATTLIST tpl:Archives
425     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
426     category  CDATA               #IMPLIED  -- Get dates of given category --
427     no_context (1|0)              #IMPLIED  -- Override context information
428     order     (asc|desc)          #IMPLIED  -- Sort asc or desc --
429     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
430     post_lang CDATA          #IMPLIED  -- Filter on the given language
431     >
432     */
433     public function Archives($attr,$content)
434     {
435          $p = "if (!isset(\$params)) \$params = array();\n";
436          $p .= "\$params['type'] = 'month';\n";
437          if (isset($attr['type'])) {
438               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
439          }
440
441          if (isset($attr['category'])) {
442               $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n";
443          }
444
445          if (isset($attr['post_type'])) {
446               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
447          }
448
449          if (isset($attr['post_lang'])) {
450               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
451          }
452
453          if (empty($attr['no_context']) && !isset($attr['category']))
454          {
455               $p .=
456               'if ($_ctx->exists("categories")) { '.
457                    "\$params['cat_id'] = \$_ctx->categories->cat_id; ".
458               "}\n";
459          }
460
461          $order = 'desc';
462          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
463               $p .= "\$params['order'] = '".$attr['order']."';\n ";
464          }
465
466          $res = "<?php\n";
467          $res .= $p;
468          $res .= $this->core->callBehavior("templatePrepareParams",
469               array("tag" => "Archives","method" => "blog::getDates"),
470               $attr,$content);
471          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
472          $res .= "?>\n";
473
474          $res .=
475          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
476
477          return $res;
478     }
479
480     /*dtd
481     <!ELEMENT tpl:ArchivesHeader - - -- First archives result container -->
482     */
483     public function ArchivesHeader($attr,$content)
484     {
485          return
486          "<?php if (\$_ctx->archives->isStart()) : ?>".
487          $content.
488          "<?php endif; ?>";
489     }
490
491     /*dtd
492     <!ELEMENT tpl:ArchivesFooter - - -- Last archives result container -->
493     */
494     public function ArchivesFooter($attr,$content)
495     {
496          return
497          "<?php if (\$_ctx->archives->isEnd()) : ?>".
498          $content.
499          "<?php endif; ?>";
500     }
501
502     /*dtd
503     <!ELEMENT tpl:ArchivesYearHeader - - -- First result of year in archives container -->
504     */
505     public function ArchivesYearHeader($attr,$content)
506     {
507          return
508          "<?php if (\$_ctx->archives->yearHeader()) : ?>".
509          $content.
510          "<?php endif; ?>";
511     }
512
513     /*dtd
514     <!ELEMENT tpl:ArchivesYearFooter - - -- Last result of year in archives container -->
515     */
516     public function ArchivesYearFooter($attr,$content)
517     {
518          return
519          "<?php if (\$_ctx->archives->yearFooter()) : ?>".
520          $content.
521          "<?php endif; ?>";
522     }
523
524     /*dtd
525     <!ELEMENT tpl:ArchiveDate - O -- Archive result date -->
526     <!ATTLIST tpl:ArchiveDate
527     format    CDATA     #IMPLIED  -- Date format (Default %B %Y) --
528     >
529     */
530     public function ArchiveDate($attr)
531     {
532          $format = '%B %Y';
533          if (!empty($attr['format'])) {
534               $format = addslashes($attr['format']);
535          }
536
537          $f = $this->getFilters($attr);
538          return '<?php echo '.sprintf($f,"dt::dt2str('".$format."',\$_ctx->archives->dt)").'; ?>';
539     }
540
541     /*dtd
542     <!ELEMENT tpl:ArchiveEntriesCount - O -- Current archive result number of entries -->
543     */
544     public function ArchiveEntriesCount($attr)
545     {
546          $f = $this->getFilters($attr);
547          return '<?php echo '.sprintf($f,'$_ctx->archives->nb_post').'; ?>';
548     }
549
550     /*dtd
551     <!ELEMENT tpl:ArchiveNext - - -- Next archive result container -->
552     <!ATTLIST tpl:ArchiveNext
553     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
554     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
555     post_lang CDATA          #IMPLIED  -- Filter on the given language
556     >
557     */
558     public function ArchiveNext($attr,$content)
559     {
560          $p = "if (!isset(\$params)) \$params = array();\n";
561          $p .= "\$params['type'] = 'month';\n";
562          if (isset($attr['type'])) {
563               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
564          }
565
566          if (isset($attr['post_type'])) {
567               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
568          }
569
570          if (isset($attr['post_lang'])) {
571               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
572          }
573
574          $p .= "\$params['next'] = \$_ctx->archives->dt;";
575
576          $res = "<?php\n";
577          $res .= $p;
578          $res .= $this->core->callBehavior("templatePrepareParams",
579               array("tag" => "ArchiveNext","method" => "blog::getDates"),
580               $attr, $content);
581          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
582          $res .= "?>\n";
583
584          $res .=
585          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
586
587          return $res;
588     }
589
590     /*dtd
591     <!ELEMENT tpl:ArchivePrevious - - -- Previous archive result container -->
592     <!ATTLIST tpl:ArchivePrevious
593     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
594     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
595     post_lang CDATA          #IMPLIED  -- Filter on the given language
596     >
597     */
598     public function ArchivePrevious($attr,$content)
599     {
600          $p = 'if (!isset($params)) $params = array();';
601          $p .= "\$params['type'] = 'month';\n";
602          if (isset($attr['type'])) {
603               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
604          }
605
606          if (isset($attr['post_type'])) {
607               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
608          }
609
610          if (isset($attr['post_lang'])) {
611               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
612          }
613
614          $p .= "\$params['previous'] = \$_ctx->archives->dt;";
615
616          $res = "<?php\n";
617          $res .= $this->core->callBehavior("templatePrepareParams",
618               array("tag" => "ArchivePrevious","method" => "blog::getDates"),
619               $attr, $content);
620          $res .= $p;
621          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
622          $res .= "?>\n";
623
624          $res .=
625          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
626
627          return $res;
628     }
629
630     /*dtd
631     <!ELEMENT tpl:ArchiveURL - O -- Current archive result URL -->
632     */
633     public function ArchiveURL($attr)
634     {
635          $f = $this->getFilters($attr);
636          return '<?php echo '.sprintf($f,'$_ctx->archives->url($core)').'; ?>';
637     }
638
639
640     /* Blog ----------------------------------------------- */
641     /*dtd
642     <!ELEMENT tpl:BlogArchiveURL - O -- Blog Archives URL -->
643     */
644     public function BlogArchiveURL($attr)
645     {
646          $f = $this->getFilters($attr);
647          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("archive")').'; ?>';
648     }
649
650     /*dtd
651     <!ELEMENT tpl:BlogCopyrightNotice - O -- Blog copyrght notices -->
652     */
653     public function BlogCopyrightNotice($attr)
654     {
655          $f = $this->getFilters($attr);
656          return '<?php echo '.sprintf($f,'$core->blog->settings->system->copyright_notice').'; ?>';
657     }
658
659     /*dtd
660     <!ELEMENT tpl:BlogDescription - O -- Blog Description -->
661     */
662     public function BlogDescription($attr)
663     {
664          $f = $this->getFilters($attr);
665          return '<?php echo '.sprintf($f,'$core->blog->desc').'; ?>';
666     }
667
668     /*dtd
669     <!ELEMENT tpl:BlogEditor - O -- Blog Editor -->
670     */
671     public function BlogEditor($attr)
672     {
673          $f = $this->getFilters($attr);
674          return '<?php echo '.sprintf($f,'$core->blog->settings->system->editor').'; ?>';
675     }
676
677     /*dtd
678     <!ELEMENT tpl:BlogFeedID - O -- Blog Feed ID -->
679     */
680     public function BlogFeedID($attr)
681     {
682          $f = $this->getFilters($attr);
683          return '<?php echo '.sprintf($f,'"urn:md5:".$core->blog->uid').'; ?>';
684     }
685
686     /*dtd
687     <!ELEMENT tpl:BlogFeedURL - O -- Blog Feed URL -->
688     <!ATTLIST tpl:BlogFeedURL
689     type (rss2|atom)    #IMPLIED  -- feed type (default : rss2)
690     >
691     */
692     public function BlogFeedURL($attr)
693     {
694          $type = !empty($attr['type']) ? $attr['type'] : 'atom';
695
696          if (!preg_match('#^(rss2|atom)$#',$type)) {
697               $type = 'atom';
698          }
699
700          $f = $this->getFilters($attr);
701          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("feed","'.$type.'")').'; ?>';
702     }
703
704     /*dtd
705     <!ELEMENT tpl:BlogName - O -- Blog Name -->
706     */
707     public function BlogName($attr)
708     {
709          $f = $this->getFilters($attr);
710          return '<?php echo '.sprintf($f,'$core->blog->name').'; ?>';
711     }
712
713     /*dtd
714     <!ELEMENT tpl:BlogLanguage - O -- Blog Language -->
715     */
716     public function BlogLanguage($attr)
717     {
718          $f = $this->getFilters($attr);
719          return '<?php echo '.sprintf($f,'$core->blog->settings->system->lang').'; ?>';
720     }
721
722     /*dtd
723     <!ELEMENT tpl:BlogThemeURL - O -- Blog's current Theme URL -->
724     */
725     public function BlogThemeURL($attr)
726     {
727          $f = $this->getFilters($attr);
728          return '<?php echo '.sprintf($f,'$core->blog->settings->system->themes_url."/".$core->blog->settings->system->theme').'; ?>';
729     }
730
731     /*dtd
732     <!ELEMENT tpl:BlogParentThemeURL - O -- Blog's current Theme's parent URL -->
733     */
734     public function BlogParentThemeURL($attr)
735     {
736          $f = $this->getFilters($attr);
737          $parent = '$core->themes->moduleInfo($core->blog->settings->system->theme,\'parent\')';
738          return '<?php echo '.sprintf($f,'$core->blog->settings->system->themes_url."/".('."$parent".' ? '."$parent".' : $core->blog->settings->system->theme)').'; ?>';
739     }
740
741     /*dtd
742     <!ELEMENT tpl:BlogPublicURL - O -- Blog Public directory URL -->
743     */
744     public function BlogPublicURL($attr)
745     {
746          $f = $this->getFilters($attr);
747          return '<?php echo '.sprintf($f,'$core->blog->settings->system->public_url').'; ?>';
748     }
749
750     /*dtd
751     <!ELEMENT tpl:BlogUpdateDate - O -- Blog last update date -->
752     <!ATTLIST tpl:BlogUpdateDate
753     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
754     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
755     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
756     >
757     */
758     public function BlogUpdateDate($attr)
759     {
760          $format = '';
761          if (!empty($attr['format'])) {
762               $format = addslashes($attr['format']);
763          } else {
764               $format = '%Y-%m-%d %H:%M:%S';
765          }
766
767          $iso8601 = !empty($attr['iso8601']);
768          $rfc822 = !empty($attr['rfc822']);
769
770          $f = $this->getFilters($attr);
771
772          if ($rfc822) {
773               return '<?php echo '.sprintf($f,"dt::rfc822(\$core->blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>';
774          } elseif ($iso8601) {
775               return '<?php echo '.sprintf($f,"dt::iso8601(\$core->blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>';
776          } else {
777               return '<?php echo '.sprintf($f,"dt::str('".$format."',\$core->blog->upddt)").'; ?>';
778          }
779     }
780
781     /*dtd
782     <!ELEMENT tpl:BlogID - 0 -- Blog ID -->
783     */
784     public function BlogID($attr)
785     {
786          $f = $this->getFilters($attr);
787          return '<?php echo '.sprintf($f,'$core->blog->id').'; ?>';
788     }
789
790     /*dtd
791     <!ELEMENT tpl:BlogRSDURL - O -- Blog RSD URL -->
792     */
793     public function BlogRSDURL($attr)
794     {
795          $f = $this->getFilters($attr);
796          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor(\'rsd\')').'; ?>';
797     }
798
799     /*dtd
800     <!ELEMENT tpl:BlogXMLRPCURL - O -- Blog XML-RPC URL -->
801     */
802     public function BlogXMLRPCURL($attr)
803     {
804          $f = $this->getFilters($attr);
805          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor(\'xmlrpc\',$core->blog->id)').'; ?>';
806     }
807
808     /*dtd
809     <!ELEMENT tpl:BlogURL - O -- Blog URL -->
810     */
811     public function BlogURL($attr)
812     {
813          $f = $this->getFilters($attr);
814          return '<?php echo '.sprintf($f,'$core->blog->url').'; ?>';
815     }
816
817     /*dtd
818     <!ELEMENT tpl:BlogQmarkURL - O -- Blog URL, ending with a question mark -->
819     */
820     public function BlogQmarkURL($attr)
821     {
822          $f = $this->getFilters($attr);
823          return '<?php echo '.sprintf($f,'$core->blog->getQmarkURL()').'; ?>';
824     }
825
826     /*dtd
827     <!ELEMENT tpl:BlogMetaRobots - O -- Blog meta robots tag definition, overrides robots_policy setting -->
828     <!ATTLIST tpl:BlogMetaRobots
829     robots    CDATA     #IMPLIED  -- can be INDEX,FOLLOW,NOINDEX,NOFOLLOW,ARCHIVE,NOARCHIVE
830     >
831     */
832     public function BlogMetaRobots($attr)
833     {
834          $robots = isset($attr['robots']) ? addslashes($attr['robots']) : '';
835          return "<?php echo context::robotsPolicy(\$core->blog->settings->system->robots_policy,'".$robots."'); ?>";
836     }
837
838     /* Categories ----------------------------------------- */
839
840     /*dtd
841     <!ELEMENT tpl:Categories - - -- Categories loop -->
842     */
843     public function Categories($attr,$content)
844     {
845          $p = "if (!isset(\$params)) \$params = array();\n";
846
847          if (isset($attr['url'])) {
848               $p .= "\$params['cat_url'] = '".addslashes($attr['url'])."';\n";
849          }
850
851          if (!empty($attr['post_type'])) {
852               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
853          }
854
855          if (!empty($attr['level'])) {
856               $p .= "\$params['level'] = ".(integer) $attr['level'].";\n";
857          }
858
859          if (isset($attr['with_empty']) && ((boolean) $attr['with_empty'] == true)) {
860               $p .= '$params[\'without_empty\'] = false;';
861          }
862
863          $res = "<?php\n";
864          $res .= $p;
865          $res .= $this->core->callBehavior("templatePrepareParams",
866               array("tag" => "Categories","method" => "blog::getCategories"),
867               $attr,$content);
868          $res .= '$_ctx->categories = $core->blog->getCategories($params);'."\n";
869          $res .= "?>\n";
870          $res .= '<?php while ($_ctx->categories->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->categories = null; unset($params); ?>';
871
872          return $res;
873     }
874
875     /*dtd
876     <!ELEMENT tpl:CategoriesHeader - - -- First Categories result container -->
877     */
878     public function CategoriesHeader($attr,$content)
879     {
880          return
881          "<?php if (\$_ctx->categories->isStart()) : ?>".
882          $content.
883          "<?php endif; ?>";
884     }
885
886     /*dtd
887     <!ELEMENT tpl:CategoriesFooter - - -- Last Categories result container -->
888     */
889     public function CategoriesFooter($attr,$content)
890     {
891          return
892          "<?php if (\$_ctx->categories->isEnd()) : ?>".
893          $content.
894          "<?php endif; ?>";
895     }
896
897     /*dtd
898     <!ELEMENT tpl:CategoryIf - - -- tests on current entry -->
899     <!ATTLIST tpl:CategoryIf
900     url       CDATA     #IMPLIED  -- category has given url
901     has_entries    (0|1)     #IMPLIED  -- post is the first post from list (value : 1) or not (value : 0)
902     has_description     (0|1)     #IMPLIED  -- category has description (value : 1) or not (value : 0)
903     >
904     */
905     public function CategoryIf($attr,$content)
906     {
907          $if = new ArrayObject();
908          $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
909
910          if (isset($attr['url'])) {
911               $url = addslashes(trim($attr['url']));
912               if (substr($url,0,1) == '!') {
913                    $url = substr($url,1);
914                    $if[] = '($_ctx->categories->cat_url != "'.$url.'")';
915               } else {
916                    $if[] = '($_ctx->categories->cat_url == "'.$url.'")';
917               }
918          }
919
920          if (isset($attr['has_entries'])) {
921               $sign = (boolean) $attr['has_entries'] ? '>' : '==';
922               $if[] = '$_ctx->categories->nb_post '.$sign.' 0';
923          }
924
925          if (isset($attr['has_description'])) {
926               $sign = (boolean) $attr['has_description'] ? '!=' : '==';
927               $if[] = '$_ctx->categories->cat_desc '.$sign.' ""';
928          }
929
930          $this->core->callBehavior('tplIfConditions','CategoryIf',$attr,$content,$if);
931
932          if (count($if) != 0) {
933               return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
934          } else {
935               return $content;
936          }
937     }
938
939     /*dtd
940     <!ELEMENT tpl:CategoryFirstChildren - - -- Current category first children loop -->
941     */
942     public function CategoryFirstChildren($attr,$content)
943     {
944          return
945          "<?php\n".
946          '$_ctx->categories = $core->blog->getCategoryFirstChildren($_ctx->categories->cat_id);'."\n".
947          'while ($_ctx->categories->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->categories = null; ?>';
948     }
949
950     /*dtd
951     <!ELEMENT tpl:CategoryParents - - -- Current category parents loop -->
952     */
953     public function CategoryParents($attr,$content)
954     {
955          return
956          "<?php\n".
957          '$_ctx->categories = $core->blog->getCategoryParents($_ctx->categories->cat_id);'."\n".
958          'while ($_ctx->categories->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->categories = null; ?>';
959     }
960
961     /*dtd
962     <!ELEMENT tpl:CategoryFeedURL - O -- Category feed URL -->
963     <!ATTLIST tpl:CategoryFeedURL
964     type (rss2|atom)    #IMPLIED  -- feed type (default : rss2)
965     >
966     */
967     public function CategoryFeedURL($attr)
968     {
969          $type = !empty($attr['type']) ? $attr['type'] : 'atom';
970
971          if (!preg_match('#^(rss2|atom)$#',$type)) {
972               $type = 'atom';
973          }
974
975          $f = $this->getFilters($attr);
976          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("feed","category/".'.
977          '$_ctx->categories->cat_url."/'.$type.'")').'; ?>';
978     }
979
980     /*dtd
981     <!ELEMENT tpl:CategoryURL - O -- Category URL (complete iabsolute URL, including blog URL) -->
982     */
983     public function CategoryURL($attr)
984     {
985          $f = $this->getFilters($attr);
986          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("category",'.
987               '$_ctx->categories->cat_url)').'; ?>';
988     }
989
990     /*dtd
991     <!ELEMENT tpl:CategoryShortURL - O -- Category short URL (relative URL, from /category/) -->
992     */
993     public function CategoryShortURL($attr)
994     {
995          $f = $this->getFilters($attr);
996          return '<?php echo '.sprintf($f,'$_ctx->categories->cat_url').'; ?>';
997     }
998
999     /*dtd
1000     <!ELEMENT tpl:CategoryDescription - O -- Category description -->
1001     */
1002     public function CategoryDescription($attr)
1003     {
1004          $f = $this->getFilters($attr);
1005          return '<?php echo '.sprintf($f,'$_ctx->categories->cat_desc').'; ?>';
1006     }
1007
1008     /*dtd
1009     <!ELEMENT tpl:CategoryTitle - O -- Category title -->
1010     */
1011     public function CategoryTitle($attr)
1012     {
1013          $f = $this->getFilters($attr);
1014          return '<?php echo '.sprintf($f,'$_ctx->categories->cat_title').'; ?>';
1015     }
1016
1017     /*dtd
1018     <!ELEMENT tpl:CategoryEntriesCount - O -- Category number of entries -->
1019     */
1020     public function CategoryEntriesCount($attr)
1021     {
1022          $f = $this->getFilters($attr);
1023          return '<?php echo '.sprintf($f,'$_ctx->categories->nb_post').'; ?>';
1024     }
1025
1026     /* Entries -------------------------------------------- */
1027     /*dtd
1028     <!ELEMENT tpl:Entries - - -- Blog Entries loop -->
1029     <!ATTLIST tpl:Entries
1030     lastn     CDATA     #IMPLIED  -- limit number of results to specified value
1031     author    CDATA     #IMPLIED  -- get entries for a given user id
1032     category  CDATA     #IMPLIED  -- get entries for specific categories only (multiple comma-separated categories can be specified. Use "!" as prefix to exclude a category)
1033     no_category    CDATA     #IMPLIED  -- get entries without category
1034     no_context (1|0)    #IMPLIED  -- Override context information
1035     sortby    (title|selected|author|date|id)    #IMPLIED  -- specify entries sort criteria (default : date) (multiple comma-separated sortby can be specified. Use "?asc" or "?desc" as suffix to provide an order for each sorby)
1036     order     (desc|asc)     #IMPLIED  -- specify entries order (default : desc)
1037     no_content     (0|1)     #IMPLIED  -- do not retrieve entries content
1038     selected  (0|1)     #IMPLIED  -- retrieve posts marked as selected only (value: 1) or not selected only (value: 0)
1039     url       CDATA     #IMPLIED  -- retrieve post by its url
1040     type      CDATA     #IMPLIED  -- retrieve post with given post_type (there can be many ones separated by comma)
1041     age       CDATA     #IMPLIED  -- retrieve posts by maximum age (ex: -2 days, last month, last week)
1042     ignore_pagination   (0|1)     #IMPLIED  -- ignore page number provided in URL (useful when using multiple tpl:Entries on the same page)
1043     >
1044     */
1045     public function Entries($attr,$content)
1046     {
1047          $lastn = -1;
1048          if (isset($attr['lastn'])) {
1049               $lastn = abs((integer) $attr['lastn'])+0;
1050          }
1051
1052          $p = 'if (!isset($_page_number)) { $_page_number = 1; }'."\n";
1053
1054          if ($lastn != 0) {
1055               // Set limit (aka nb of entries needed)
1056               if ($lastn > 0) {
1057                    // nb of entries per page specified in template -> regular pagination
1058                    $p .= "\$params['limit'] = ".$lastn.";\n";
1059               } else {
1060                    // nb of entries per page not specified -> use ctx settings
1061                    $p .= "\$params['limit'] = (\$_page_number == 1 ? \$_ctx->nb_entry_first_page : \$_ctx->nb_entry_per_page);\n";
1062               }
1063               // Set offset (aka index of first entry)
1064               if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == "0") {
1065                    // standard pagination, set offset
1066                    $p .= "\$params['limit'] = array((\$_page_number == 1 ? 0 : (\$_page_number - 2) * \$_ctx->nb_entry_per_page + \$_ctx->nb_entry_first_page),\$params['limit']);\n";
1067               } else {
1068                    // no pagination, get all posts from 0 to limit
1069                    $p .= "\$params['limit'] = array(0, \$params['limit']);\n";
1070               }
1071          }
1072
1073          if (isset($attr['author'])) {
1074               $p .= "\$params['user_id'] = '".addslashes($attr['author'])."';\n";
1075          }
1076
1077          if (isset($attr['category'])) {
1078               $p .= "\$params['cat_url'] = '".addslashes($attr['category'])."';\n";
1079               $p .= "context::categoryPostParam(\$params);\n";
1080          }
1081
1082          if (isset($attr['no_category']) && $attr['no_category']) {
1083               $p .= "@\$params['sql'] .= ' AND P.cat_id IS NULL ';\n";
1084               $p .= "unset(\$params['cat_url']);\n";
1085          }
1086
1087          if (!empty($attr['type'])) {
1088               $p .= "\$params['post_type'] = preg_split('/\s*,\s*/','".addslashes($attr['type'])."',-1,PREG_SPLIT_NO_EMPTY);\n";
1089          }
1090
1091          if (!empty($attr['url'])) {
1092               $p .= "\$params['post_url'] = '".addslashes($attr['url'])."';\n";
1093          }
1094
1095          if (empty($attr['no_context']))
1096          {
1097               if (!isset($attr['author']))
1098               {
1099                    $p .=
1100                    'if ($_ctx->exists("users")) { '.
1101                         "\$params['user_id'] = \$_ctx->users->user_id; ".
1102                    "}\n";
1103               }
1104
1105               if (!isset($attr['category']) && (!isset($attr['no_category']) || !$attr['no_category']))
1106               {
1107                    $p .=
1108                    'if ($_ctx->exists("categories")) { '.
1109                         "\$params['cat_id'] = \$_ctx->categories->cat_id.(\$core->blog->settings->system->inc_subcats?' ?sub':'');".
1110                    "}\n";
1111               }
1112
1113               $p .=
1114               'if ($_ctx->exists("archives")) { '.
1115                    "\$params['post_year'] = \$_ctx->archives->year(); ".
1116                    "\$params['post_month'] = \$_ctx->archives->month(); ";
1117               if (!isset($attr['lastn'])) {
1118                    $p .= "unset(\$params['limit']); ";
1119               }
1120               $p .=
1121               "}\n";
1122
1123               $p .=
1124               'if ($_ctx->exists("langs")) { '.
1125                    "\$params['post_lang'] = \$_ctx->langs->post_lang; ".
1126               "}\n";
1127
1128               $p .=
1129               'if (isset($_search)) { '.
1130                    "\$params['search'] = \$_search; ".
1131               "}\n";
1132          }
1133
1134          $p .= "\$params['order'] = '".$this->getSortByStr($attr,'post')."';\n";
1135
1136          if (isset($attr['no_content']) && $attr['no_content']) {
1137               $p .= "\$params['no_content'] = true;\n";
1138          }
1139
1140          if (isset($attr['selected'])) {
1141               $p .= "\$params['post_selected'] = ".(integer) (boolean) $attr['selected'].";";
1142          }
1143
1144          if (isset($attr['age'])) {
1145               $age = $this->getAge($attr);
1146               $p .= !empty($age) ? "@\$params['sql'] .= ' AND P.post_dt > \'".$age."\'';\n" : '';
1147          }
1148
1149          $res = "<?php\n";
1150          $res .= $p;
1151          $res .= $this->core->callBehavior("templatePrepareParams",
1152               array("tag" => "Entries","method" => "blog::getPosts"),
1153               $attr,$content);
1154          $res .= '$_ctx->post_params = $params;'."\n";
1155          $res .= '$_ctx->posts = $core->blog->getPosts($params); unset($params);'."\n";
1156          $res .= "?>\n";
1157          $res .=
1158          '<?php while ($_ctx->posts->fetch()) : ?>'.$content.'<?php endwhile; '.
1159          '$_ctx->posts = null; $_ctx->post_params = null; ?>';
1160
1161          return $res;
1162     }
1163
1164     /*dtd
1165     <!ELEMENT tpl:DateHeader - O -- Displays date, if post is the first post of the given day -->
1166     */
1167     public function DateHeader($attr,$content)
1168     {
1169          return
1170          "<?php if (\$_ctx->posts->firstPostOfDay()) : ?>".
1171          $content.
1172          "<?php endif; ?>";
1173     }
1174
1175     /*dtd
1176     <!ELEMENT tpl:DateFooter - O -- Displays date,  if post is the last post of the given day -->
1177     */
1178     public function DateFooter($attr,$content)
1179     {
1180          return
1181          "<?php if (\$_ctx->posts->lastPostOfDay()) : ?>".
1182          $content.
1183          "<?php endif; ?>";
1184     }
1185
1186     /*dtd
1187     <!ELEMENT tpl:EntryIf - - -- tests on current entry -->
1188     <!ATTLIST tpl:EntryIf
1189     type CDATA     #IMPLIED  -- post has a given type (default: "post")
1190     category  CDATA     #IMPLIED  -- post has a given category
1191     first     (0|1)     #IMPLIED  -- post is the first post from list (value : 1) or not (value : 0)
1192     odd  (0|1)     #IMPLIED  -- post is in an odd position (value : 1) or not (value : 0)
1193     even (0|1)     #IMPLIED  -- post is in an even position (value : 1) or not (value : 0)
1194     extended  (0|1)     #IMPLIED  -- post has an excerpt (value : 1) or not (value : 0)
1195     selected  (0|1)     #IMPLIED  -- post is selected (value : 1) or not (value : 0)
1196     has_category   (0|1)     #IMPLIED  -- post has a category (value : 1) or not (value : 0)
1197     has_attachment (0|1)     #IMPLIED  -- post has attachments (value : 1) or not (value : 0) (see Attachment plugin for code)
1198     comments_active     (0|1)     #IMPLIED  -- comments are active for this post (value : 1) or not (value : 0)
1199     pings_active   (0|1)     #IMPLIED  -- trackbacks are active for this post (value : 1) or not (value : 0)
1200     show_comments  (0|1)     #IMPLIED  -- there are comments for this post (value : 1) or not (value : 0)
1201     show_pings     (0|1)     #IMPLIED  -- there are trackbacks for this post (value : 1) or not (value : 0)
1202     republished    (0|1)     #IMPLIED  -- post has been updated since publication (value : 1) or not (value : 0)
1203     operator  (and|or)  #IMPLIED  -- combination of conditions, if more than 1 specifiec (default: and)
1204     url       CDATA     #IMPLIED  -- post has given url
1205     >
1206     */
1207     public function EntryIf($attr,$content)
1208     {
1209          $if = new ArrayObject();
1210          $extended = null;
1211          $hascategory = null;
1212
1213          $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
1214
1215          if (isset($attr['type'])) {
1216               $type = trim($attr['type']);
1217               $type = !empty($type)?$type:'post';
1218               $if[] = '$_ctx->posts->post_type == "'.addslashes($type).'"';
1219          }
1220
1221          if (isset($attr['url'])) {
1222               $url = trim($attr['url']);
1223               if (substr($url,0,1) == '!') {
1224                    $url = substr($url,1);
1225                    $if[] = '$_ctx->posts->post_url != "'.addslashes($url).'"';
1226               } else {
1227                    $if[] = '$_ctx->posts->post_url == "'.addslashes($url).'"';
1228               }
1229          }
1230
1231          if (isset($attr['category'])) {
1232               $category = addslashes(trim($attr['category']));
1233               if (substr($category,0,1) == '!') {
1234                    $category = substr($category,1);
1235                    $if[] = '($_ctx->posts->cat_url != "'.$category.'")';
1236               } else {
1237                    $if[] = '($_ctx->posts->cat_url == "'.$category.'")';
1238               }
1239          }
1240
1241          if (isset($attr['first'])) {
1242               $sign = (boolean) $attr['first'] ? '=' : '!';
1243               $if[] = '$_ctx->posts->index() '.$sign.'= 0';
1244          }
1245
1246          if (isset($attr['odd'])) {
1247               $sign = (boolean) $attr['odd'] ? '=' : '!';
1248               $if[] = '($_ctx->posts->index()+1)%2 '.$sign.'= 1';
1249          }
1250
1251          if (isset($attr['extended'])) {
1252               $sign = (boolean) $attr['extended'] ? '' : '!';
1253               $if[] = $sign.'$_ctx->posts->isExtended()';
1254          }
1255
1256          if (isset($attr['selected'])) {
1257               $sign = (boolean) $attr['selected'] ? '' : '!';
1258               $if[] = $sign.'(boolean)$_ctx->posts->post_selected';
1259          }
1260
1261          if (isset($attr['has_category'])) {
1262               $sign = (boolean) $attr['has_category'] ? '' : '!';
1263               $if[] = $sign.'$_ctx->posts->cat_id';
1264          }
1265
1266          if (isset($attr['comments_active'])) {
1267               $sign = (boolean) $attr['comments_active'] ? '' : '!';
1268               $if[] = $sign.'$_ctx->posts->commentsActive()';
1269          }
1270
1271          if (isset($attr['pings_active'])) {
1272               $sign = (boolean) $attr['pings_active'] ? '' : '!';
1273               $if[] = $sign.'$_ctx->posts->trackbacksActive()';
1274          }
1275
1276          if (isset($attr['has_comment'])) {
1277               $sign = (boolean) $attr['has_comment'] ? '' : '!';
1278               $if[] = $sign.'$_ctx->posts->hasComments()';
1279          }
1280
1281          if (isset($attr['has_ping'])) {
1282               $sign = (boolean) $attr['has_ping'] ? '' : '!';
1283               $if[] = $sign.'$_ctx->posts->hasTrackbacks()';
1284          }
1285
1286          if (isset($attr['show_comments'])) {
1287               if ((boolean) $attr['show_comments']) {
1288                    $if[] = '($_ctx->posts->hasComments() || $_ctx->posts->commentsActive())';
1289               } else {
1290                    $if[] = '(!$_ctx->posts->hasComments() && !$_ctx->posts->commentsActive())';
1291               }
1292          }
1293
1294          if (isset($attr['show_pings'])) {
1295               if ((boolean) $attr['show_pings']) {
1296                    $if[] = '($_ctx->posts->hasTrackbacks() || $_ctx->posts->trackbacksActive())';
1297               } else {
1298                    $if[] = '(!$_ctx->posts->hasTrackbacks() && !$_ctx->posts->trackbacksActive())';
1299               }
1300          }
1301
1302          if (isset($attr['republished'])) {
1303               $sign = (boolean) $attr['republished'] ? '' : '!';
1304               $if[] = $sign.'(boolean)$_ctx->posts->isRepublished()';
1305          }
1306
1307          $this->core->callBehavior('tplIfConditions','EntryIf',$attr,$content,$if);
1308
1309          if (count($if) != 0) {
1310               return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
1311          } else {
1312               return $content;
1313          }
1314     }
1315
1316     /*dtd
1317     <!ELEMENT tpl:EntryIfFirst - O -- displays value if entry is the first one -->
1318     <!ATTLIST tpl:EntryIfFirst
1319     return    CDATA     #IMPLIED  -- value to display in case of success (default: first)
1320     >
1321     */
1322     public function EntryIfFirst($attr)
1323     {
1324          $ret = isset($attr['return']) ? $attr['return'] : 'first';
1325          $ret = html::escapeHTML($ret);
1326
1327          return
1328          '<?php if ($_ctx->posts->index() == 0) { '.
1329          "echo '".addslashes($ret)."'; } ?>";
1330     }
1331
1332     /*dtd
1333     <!ELEMENT tpl:EntryIfOdd - O -- displays value if entry is in an odd position -->
1334     <!ATTLIST tpl:EntryIfOdd
1335     return    CDATA     #IMPLIED  -- value to display in case of success (default: odd)
1336     >
1337     */
1338     public function EntryIfOdd($attr)
1339     {
1340          $ret = isset($attr['return']) ? $attr['return'] : 'odd';
1341          $ret = html::escapeHTML($ret);
1342
1343          return
1344          '<?php if (($_ctx->posts->index()+1)%2 == 1) { '.
1345          "echo '".addslashes($ret)."'; } ?>";
1346     }
1347
1348     /*dtd
1349     <!ELEMENT tpl:EntryIfSelected - O -- displays value if entry is selected -->
1350     <!ATTLIST tpl:EntryIfSelected
1351     return    CDATA     #IMPLIED  -- value to display in case of success (default: selected)
1352     >
1353     */
1354     public function EntryIfSelected($attr)
1355     {
1356          $ret = isset($attr['return']) ? $attr['return'] : 'selected';
1357          $ret = html::escapeHTML($ret);
1358
1359          return
1360          '<?php if ($_ctx->posts->post_selected) { '.
1361          "echo '".addslashes($ret)."'; } ?>";
1362     }
1363
1364     /*dtd
1365     <!ELEMENT tpl:EntryContent - O -- Entry content -->
1366     <!ATTLIST tpl:EntryContent
1367     absolute_urls  CDATA     #IMPLIED -- transforms local URLs to absolute one
1368     full           (1|0)     #IMPLIED -- returns full content with excerpt
1369     >
1370     */
1371     public function EntryContent($attr)
1372     {
1373          $urls = '0';
1374          if (!empty($attr['absolute_urls'])) {
1375               $urls = '1';
1376          }
1377
1378          $f = $this->getFilters($attr);
1379
1380          if (!empty($attr['full'])) {
1381               return '<?php echo '.sprintf($f,
1382                    '$_ctx->posts->getExcerpt('.$urls.')." ".$_ctx->posts->getContent('.$urls.')').'; ?>';
1383          } else {
1384               return '<?php echo '.sprintf($f,'$_ctx->posts->getContent('.$urls.')').'; ?>';
1385          }
1386     }
1387
1388     /*dtd
1389     <!ELEMENT tpl:EntryExcerpt - O -- Entry excerpt -->
1390     <!ATTLIST tpl:EntryExcerpt
1391     absolute_urls  CDATA     #IMPLIED -- transforms local URLs to absolute one
1392     >
1393     */
1394     public function EntryExcerpt($attr)
1395     {
1396          $urls = '0';
1397          if (!empty($attr['absolute_urls'])) {
1398               $urls = '1';
1399          }
1400
1401          $f = $this->getFilters($attr);
1402          return '<?php echo '.sprintf($f,'$_ctx->posts->getExcerpt('.$urls.')').'; ?>';
1403     }
1404
1405
1406     /*dtd
1407     <!ELEMENT tpl:EntryAuthorCommonName - O -- Entry author common name -->
1408     */
1409     public function EntryAuthorCommonName($attr)
1410     {
1411          $f = $this->getFilters($attr);
1412          return '<?php echo '.sprintf($f,'$_ctx->posts->getAuthorCN()').'; ?>';
1413     }
1414
1415     /*dtd
1416     <!ELEMENT tpl:EntryAuthorDisplayName - O -- Entry author display name -->
1417     */
1418     public function EntryAuthorDisplayName($attr)
1419     {
1420          $f = $this->getFilters($attr);
1421          return '<?php echo '.sprintf($f,'$_ctx->posts->user_displayname').'; ?>';
1422     }
1423
1424     /*dtd
1425     <!ELEMENT tpl:EntryAuthorID - O -- Entry author ID -->
1426     */
1427     public function EntryAuthorID($attr)
1428     {
1429          $f = $this->getFilters($attr);
1430          return '<?php echo '.sprintf($f,'$_ctx->posts->user_id').'; ?>';
1431     }
1432
1433     /*dtd
1434     <!ELEMENT tpl:EntryAuthorEmail - O -- Entry author email -->
1435     <!ATTLIST tpl:EntryAuthorEmail
1436     spam_protected (0|1)     #IMPLIED  -- protect email from spam (default: 1)
1437     >
1438     */
1439     public function EntryAuthorEmail($attr)
1440     {
1441          $p = 'true';
1442          if (isset($attr['spam_protected']) && !$attr['spam_protected']) {
1443               $p = 'false';
1444          }
1445
1446          $f = $this->getFilters($attr);
1447          return '<?php echo '.sprintf($f,"\$_ctx->posts->getAuthorEmail(".$p.")").'; ?>';
1448     }
1449
1450     /*dtd
1451     <!ELEMENT tpl:EntryAuthorLink - O -- Entry author link -->
1452     */
1453     public function EntryAuthorLink($attr)
1454     {
1455          $f = $this->getFilters($attr);
1456          return '<?php echo '.sprintf($f,'$_ctx->posts->getAuthorLink()').'; ?>';
1457     }
1458
1459     /*dtd
1460     <!ELEMENT tpl:EntryAuthorURL - O -- Entry author URL -->
1461     */
1462     public function EntryAuthorURL($attr)
1463     {
1464          $f = $this->getFilters($attr);
1465          return '<?php echo '.sprintf($f,'$_ctx->posts->user_url').'; ?>';
1466     }
1467
1468     /*dtd
1469     <!ELEMENT tpl:EntryBasename - O -- Entry short URL (relative to /post) -->
1470     */
1471     public function EntryBasename($attr)
1472     {
1473          $f = $this->getFilters($attr);
1474          return '<?php echo '.sprintf($f,'$_ctx->posts->post_url').'; ?>';
1475     }
1476
1477     /*dtd
1478     <!ELEMENT tpl:EntryCategory - O -- Entry category (full name) -->
1479     */
1480     public function EntryCategory($attr)
1481     {
1482          $f = $this->getFilters($attr);
1483          return '<?php echo '.sprintf($f,'$_ctx->posts->cat_title').'; ?>';
1484     }
1485
1486     /*dtd
1487     <!ELEMENT tpl:EntryCategoriesBreadcrumb - - -- Current entry parents loop (without last one) -->
1488     */
1489     public function EntryCategoriesBreadcrumb($attr,$content)
1490     {
1491          return
1492          "<?php\n".
1493          '$_ctx->categories = $core->blog->getCategoryParents($_ctx->posts->cat_id);'."\n".
1494          'while ($_ctx->categories->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->categories = null; ?>';
1495     }
1496
1497     /*dtd
1498     <!ELEMENT tpl:EntryCategoryID - O -- Entry category ID -->
1499     */
1500     public function EntryCategoryID($attr)
1501     {
1502          $f = $this->getFilters($attr);
1503          return '<?php echo '.sprintf($f,'$_ctx->posts->cat_id').'; ?>';
1504     }
1505
1506     /*dtd
1507     <!ELEMENT tpl:EntryCategoryURL - O -- Entry category URL -->
1508     */
1509     public function EntryCategoryURL($attr)
1510     {
1511          $f = $this->getFilters($attr);
1512          return '<?php echo '.sprintf($f,'$_ctx->posts->getCategoryURL()').'; ?>';
1513     }
1514
1515     /*dtd
1516     <!ELEMENT tpl:EntryCategoryShortURL - O -- Entry category short URL (relative URL, from /category/) -->
1517     */
1518     public function EntryCategoryShortURL($attr)
1519     {
1520          $f = $this->getFilters($attr);
1521          return '<?php echo '.sprintf($f,'$_ctx->posts->cat_url').'; ?>';
1522     }
1523
1524
1525     /*dtd
1526     <!ELEMENT tpl:EntryFeedID - O -- Entry feed ID -->
1527     */
1528     public function EntryFeedID($attr)
1529     {
1530          $f = $this->getFilters($attr);
1531          return '<?php echo '.sprintf($f,'$_ctx->posts->getFeedID()').'; ?>';
1532     }
1533
1534     /*dtd
1535     <!ELEMENT tpl:EntryFirstImage - O -- Extracts entry first image if exists -->
1536     <!ATTLIST tpl:EntryAuthorEmail
1537     size           (sq|t|s|m|o)   #IMPLIED  -- Image size to extract
1538     class          CDATA          #IMPLIED  -- Class to add on image tag
1539     with_category  (1|0)          #IMPLIED  -- Search in entry category description if present (default 0)
1540     no_tag    (1|0)     #IMPLIED  -- Return image URL without HTML tag (default 0)
1541     content_only   (1|0)          #IMPLIED  -- Search in content entry only, not in excerpt (default 0)
1542     cat_only  (1|0)          #IMPLIED  -- Search in category description only (default 0)
1543     >
1544     */
1545     public function EntryFirstImage($attr)
1546     {
1547          $size = !empty($attr['size']) ? $attr['size'] : '';
1548          $class = !empty($attr['class']) ? $attr['class'] : '';
1549          $with_category = !empty($attr['with_category']) ? 1 : 0;
1550          $no_tag = !empty($attr['no_tag']) ? 1 : 0;
1551          $content_only = !empty($attr['content_only']) ? 1 : 0;
1552          $cat_only = !empty($attr['cat_only']) ? 1 : 0;
1553
1554          return "<?php echo context::EntryFirstImageHelper('".addslashes($size)."',".$with_category.",'".addslashes($class)."',".
1555               $no_tag.",".$content_only.",".$cat_only."); ?>";
1556     }
1557
1558     /*dtd
1559     <!ELEMENT tpl:EntryID - O -- Entry ID -->
1560     */
1561     public function EntryID($attr)
1562     {
1563          $f = $this->getFilters($attr);
1564          return '<?php echo '.sprintf($f,'$_ctx->posts->post_id').'; ?>';
1565     }
1566
1567     /*dtd
1568     <!ELEMENT tpl:EntryLang - O --  Entry language or blog lang if not defined -->
1569     */
1570     public function EntryLang($attr)
1571     {
1572          $f = $this->getFilters($attr);
1573          return
1574          '<?php if ($_ctx->posts->post_lang) { '.
1575               'echo '.sprintf($f,'$_ctx->posts->post_lang').'; '.
1576          '} else {'.
1577               'echo '.sprintf($f,'$core->blog->settings->system->lang').'; '.
1578          '} ?>';
1579     }
1580
1581     /*dtd
1582     <!ELEMENT tpl:EntryNext - - -- Next entry block -->
1583     <!ATTLIST tpl:EntryNext
1584     restrict_to_category     (0|1)     #IMPLIED  -- find next post in the same category (default: 0)
1585     restrict_to_lang         (0|1)     #IMPLIED  -- find next post in the same language (default: 0)
1586     >
1587     */
1588     public function EntryNext($attr,$content)
1589     {
1590          $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0';
1591          $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0';
1592
1593          return
1594          '<?php $next_post = $core->blog->getNextPost($_ctx->posts,1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n".
1595          '<?php if ($next_post !== null) : ?>'.
1596
1597               '<?php $_ctx->posts = $next_post; unset($next_post);'."\n".
1598               'while ($_ctx->posts->fetch()) : ?>'.
1599               $content.
1600               '<?php endwhile; $_ctx->posts = null; ?>'.
1601          "<?php endif; ?>\n";
1602     }
1603
1604     /*dtd
1605     <!ELEMENT tpl:EntryPrevious - - -- Previous entry block -->
1606     <!ATTLIST tpl:EntryPrevious
1607     restrict_to_category     (0|1)     #IMPLIED  -- find previous post in the same category (default: 0)
1608     restrict_to_lang         (0|1)     #IMPLIED  -- find next post in the same language (default: 0)
1609     >
1610     */
1611     public function EntryPrevious($attr,$content)
1612     {
1613          $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0';
1614          $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0';
1615
1616          return
1617          '<?php $prev_post = $core->blog->getNextPost($_ctx->posts,-1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n".
1618          '<?php if ($prev_post !== null) : ?>'.
1619
1620               '<?php $_ctx->posts = $prev_post; unset($prev_post);'."\n".
1621               'while ($_ctx->posts->fetch()) : ?>'.
1622               $content.
1623               '<?php endwhile; $_ctx->posts = null; ?>'.
1624          "<?php endif; ?>\n";
1625     }
1626
1627     /*dtd
1628     <!ELEMENT tpl:EntryTitle - O -- Entry title -->
1629     */
1630     public function EntryTitle($attr)
1631     {
1632          $f = $this->getFilters($attr);
1633          return '<?php echo '.sprintf($f,'$_ctx->posts->post_title').'; ?>';
1634     }
1635
1636     /*dtd
1637     <!ELEMENT tpl:EntryURL - O -- Entry URL -->
1638     */
1639     public function EntryURL($attr)
1640     {
1641          $f = $this->getFilters($attr);
1642          return '<?php echo '.sprintf($f,'$_ctx->posts->getURL()').'; ?>';
1643     }
1644
1645     /*dtd
1646     <!ELEMENT tpl:EntryDate - O -- Entry date -->
1647     <!ATTLIST tpl:EntryDate
1648     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
1649     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
1650     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
1651     upddt     CDATA     #IMPLIED  -- if set, uses the post update time
1652     creadt    CDATA     #IMPLIED  -- if set, uses the post creation time
1653     >
1654     */
1655     public function EntryDate($attr)
1656     {
1657          $format = '';
1658          if (!empty($attr['format'])) {
1659               $format = addslashes($attr['format']);
1660          }
1661
1662          $iso8601 = !empty($attr['iso8601']);
1663          $rfc822 = !empty($attr['rfc822']);
1664          $type = (!empty($attr['creadt']) ? 'creadt' : '');
1665          $type = (!empty($attr['upddt']) ? 'upddt' : $type);
1666
1667          $f = $this->getFilters($attr);
1668
1669          if ($rfc822) {
1670               return '<?php echo '.sprintf($f,"\$_ctx->posts->getRFC822Date('".$type."')").'; ?>';
1671          } elseif ($iso8601) {
1672               return '<?php echo '.sprintf($f,"\$_ctx->posts->getISO8601Date('".$type."')").'; ?>';
1673          } else {
1674               return '<?php echo '.sprintf($f,"\$_ctx->posts->getDate('".$format."','".$type."')").'; ?>';
1675          }
1676     }
1677
1678     /*dtd
1679     <!ELEMENT tpl:EntryTime - O -- Entry date -->
1680     <!ATTLIST tpl:EntryTime
1681     format    CDATA     #IMPLIED  -- time format
1682     upddt     CDATA     #IMPLIED  -- if set, uses the post update time
1683     creadt    CDATA     #IMPLIED  -- if set, uses the post creation time
1684     >
1685     */
1686     public function EntryTime($attr)
1687     {
1688          $format = '';
1689          if (!empty($attr['format'])) {
1690               $format = addslashes($attr['format']);
1691          }
1692
1693          $type = (!empty($attr['creadt']) ? 'creadt' : '');
1694          $type = (!empty($attr['upddt']) ? 'upddt' : $type);
1695
1696          $f = $this->getFilters($attr);
1697          return '<?php echo '.sprintf($f,"\$_ctx->posts->getTime('".$format."','".$type."')").'; ?>';
1698     }
1699
1700     /*dtd
1701     <!ELEMENT tpl:EntriesHeader - - -- First entries result container -->
1702     */
1703     public function EntriesHeader($attr,$content)
1704     {
1705          return
1706          "<?php if (\$_ctx->posts->isStart()) : ?>".
1707          $content.
1708          "<?php endif; ?>";
1709     }
1710
1711     /*dtd
1712     <!ELEMENT tpl:EntriesFooter - - -- Last entries result container -->
1713     */
1714     public function EntriesFooter($attr,$content)
1715     {
1716          return
1717          "<?php if (\$_ctx->posts->isEnd()) : ?>".
1718          $content.
1719          "<?php endif; ?>";
1720     }
1721
1722     /*dtd
1723     <!ELEMENT tpl:EntryCommentCount - O -- Number of comments for entry -->
1724     <!ATTLIST tpl:EntryCommentCount
1725     none      CDATA     #IMPLIED  -- text to display for "no comment" (default: no comment)
1726     one       CDATA     #IMPLIED  -- text to display for "one comment" (default: one comment)
1727     more      CDATA     #IMPLIED  -- text to display for "more comments" (default: %s comments, %s is replaced by the number of comment)
1728     count_all CDATA     #IMPLIED  -- count comments and trackbacks
1729     >
1730     */
1731     public function EntryCommentCount($attr)
1732     {
1733          $none = 'no comment';
1734          $one = 'one comment';
1735          $more = '%d comments';
1736
1737          if (isset($attr['none'])) {
1738               $none = addslashes($attr['none']);
1739          }
1740          if (isset($attr['one'])) {
1741               $one = addslashes($attr['one']);
1742          }
1743          if (isset($attr['more'])) {
1744               $more = addslashes($attr['more']);
1745          }
1746
1747          if (empty($attr['count_all'])) {
1748               $operation = '$_ctx->posts->nb_comment';
1749          } else {
1750               $operation = '($_ctx->posts->nb_comment + $_ctx->posts->nb_trackback)';
1751          }
1752
1753          return
1754          "<?php if (".$operation." == 0) {\n".
1755          "  printf(__('".$none."'),".$operation.");\n".
1756          "} elseif (".$operation." == 1) {\n".
1757          "  printf(__('".$one."'),".$operation.");\n".
1758          "} else {\n".
1759          "  printf(__('".$more."'),".$operation.");\n".
1760          "} ?>";
1761     }
1762
1763     /*dtd
1764     <!ELEMENT tpl:EntryPingCount - O -- Number of trackbacks for entry -->
1765     <!ATTLIST tpl:EntryPingCount
1766     none CDATA     #IMPLIED  -- text to display for "no ping" (default: no ping)
1767     one  CDATA     #IMPLIED  -- text to display for "one ping" (default: one ping)
1768     more CDATA     #IMPLIED  -- text to display for "more pings" (default: %s trackbacks, %s is replaced by the number of pings)
1769     >
1770     */
1771     public function EntryPingCount($attr)
1772     {
1773          $none = 'no trackback';
1774          $one = 'one trackback';
1775          $more = '%d trackbacks';
1776
1777          if (isset($attr['none'])) {
1778               $none = addslashes($attr['none']);
1779          }
1780          if (isset($attr['one'])) {
1781               $one = addslashes($attr['one']);
1782          }
1783          if (isset($attr['more'])) {
1784               $more = addslashes($attr['more']);
1785          }
1786
1787          return
1788          "<?php if (\$_ctx->posts->nb_trackback == 0) {\n".
1789          "  printf(__('".$none."'),(integer) \$_ctx->posts->nb_trackback);\n".
1790          "} elseif (\$_ctx->posts->nb_trackback == 1) {\n".
1791          "  printf(__('".$one."'),(integer) \$_ctx->posts->nb_trackback);\n".
1792          "} else {\n".
1793          "  printf(__('".$more."'),(integer) \$_ctx->posts->nb_trackback);\n".
1794          "} ?>";
1795     }
1796
1797     /*dtd
1798     <!ELEMENT tpl:EntryPingData - O -- Display trackback RDF information -->
1799     */
1800     public function EntryPingData($attr)
1801     {
1802          return "<?php if (\$_ctx->posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackData(); } ?>\n";
1803     }
1804
1805     /*dtd
1806     <!ELEMENT tpl:EntryPingLink - O -- Entry trackback link -->
1807     */
1808     public function EntryPingLink($attr)
1809     {
1810          return "<?php if (\$_ctx->posts->trackbacksActive()) { echo \$_ctx->posts->getTrackbackLink(); } ?>\n";
1811     }
1812
1813     /* Languages -------------------------------------- */
1814     /*dtd
1815     <!ELEMENT tpl:Languages - - -- Languages loop -->
1816     <!ATTLIST tpl:Languages
1817     lang CDATA     #IMPLIED  -- restrict loop on given lang
1818     order     (desc|asc)     #IMPLIED  -- languages ordering (default: desc)
1819     >
1820     */
1821     public function Languages($attr,$content)
1822     {
1823          $p = "if (!isset(\$params)) \$params = array();\n";
1824
1825          if (isset($attr['lang'])) {
1826               $p = "\$params['lang'] = '".addslashes($attr['lang'])."';\n";
1827          }
1828
1829          $order = 'desc';
1830          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
1831               $p .= "\$params['order'] = '".$attr['order']."';\n ";
1832          }
1833
1834          $res = "<?php\n";
1835          $res .= $p;
1836          $res .= $this->core->callBehavior("templatePrepareParams",
1837               array("tag" => "Languages","method" => "blog::getLangs"),
1838               $attr,$content);
1839          $res .= '$_ctx->langs = $core->blog->getLangs($params); unset($params);'."\n";
1840          $res .= "?>\n";
1841
1842          $res .=
1843          '<?php if ($_ctx->langs->count() > 1) : '.
1844          'while ($_ctx->langs->fetch()) : ?>'.$content.
1845          '<?php endwhile; $_ctx->langs = null; endif; ?>';
1846
1847          return $res;
1848     }
1849
1850     /*dtd
1851     <!ELEMENT tpl:LanguagesHeader - - -- First languages result container -->
1852     */
1853     public function LanguagesHeader($attr,$content)
1854     {
1855          return
1856          "<?php if (\$_ctx->langs->isStart()) : ?>".
1857          $content.
1858          "<?php endif; ?>";
1859     }
1860
1861     /*dtd
1862     <!ELEMENT tpl:LanguagesFooter - - -- Last languages result container -->
1863     */
1864     public function LanguagesFooter($attr,$content)
1865     {
1866          return
1867          "<?php if (\$_ctx->langs->isEnd()) : ?>".
1868          $content.
1869          "<?php endif; ?>";
1870     }
1871
1872     /*dtd
1873     <!ELEMENT tpl:LanguageCode - O -- Language code -->
1874     */
1875     public function LanguageCode($attr)
1876     {
1877          $f = $this->getFilters($attr);
1878          return '<?php echo '.sprintf($f,'$_ctx->langs->post_lang').'; ?>';
1879     }
1880
1881     /*dtd
1882     <!ELEMENT tpl:LanguageIfCurrent - - -- tests if post language is current language -->
1883     */
1884     public function LanguageIfCurrent($attr,$content)
1885     {
1886          return
1887          "<?php if (\$_ctx->cur_lang == \$_ctx->langs->post_lang) : ?>".
1888          $content.
1889          "<?php endif; ?>";
1890     }
1891
1892     /*dtd
1893     <!ELEMENT tpl:LanguageURL - O -- Language URL -->
1894     */
1895     public function LanguageURL($attr)
1896     {
1897          $f = $this->getFilters($attr);
1898          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("lang",'.
1899               '$_ctx->langs->post_lang)').'; ?>';
1900     }
1901
1902     /* Pagination ------------------------------------- */
1903     /*dtd
1904     <!ELEMENT tpl:Pagination - - -- Pagination container -->
1905     <!ATTLIST tpl:Pagination
1906     no_context     (0|1)     #IMPLIED  -- override test on posts count vs number of posts per page
1907     >
1908     */
1909     public function Pagination($attr,$content)
1910     {
1911          $p = "<?php\n";
1912          $p .= '$params = $_ctx->post_params;'."\n";
1913          $p .= $this->core->callBehavior("templatePrepareParams",
1914               array("tag" => "Pagination","method" => "blog::getPosts"),
1915               $attr,$content);
1916          $p .= '$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);'."\n";
1917          $p .= "?>\n";
1918
1919          if (isset($attr['no_context']) && $attr['no_context']) {
1920               return $p.$content;
1921          }
1922
1923          return
1924               $p.
1925               '<?php if ($_ctx->pagination->f(0) > $_ctx->posts->count()) : ?>'.
1926               $content.
1927               '<?php endif; ?>';
1928     }
1929
1930     /*dtd
1931     <!ELEMENT tpl:PaginationCounter - O -- Number of pages -->
1932     */
1933     public function PaginationCounter($attr)
1934     {
1935          $f = $this->getFilters($attr);
1936          return '<?php echo '.sprintf($f,"context::PaginationNbPages()").'; ?>';
1937     }
1938
1939     /*dtd
1940     <!ELEMENT tpl:PaginationCurrent - O -- current page -->
1941     */
1942     public function PaginationCurrent($attr)
1943     {
1944          $offset = 0;
1945          if (isset($attr['offset'])) {
1946               $offset = (integer) $attr['offset'];
1947          }
1948
1949          $f = $this->getFilters($attr);
1950          return '<?php echo '.sprintf($f,"context::PaginationPosition(".$offset.")").'; ?>';
1951     }
1952
1953     /*dtd
1954     <!ELEMENT tpl:PaginationIf - - -- pages tests -->
1955     <!ATTLIST tpl:PaginationIf
1956     start     (0|1)     #IMPLIED  -- test if we are at first page (value : 1) or not (value : 0)
1957     end  (0|1)     #IMPLIED  -- test if we are at last page (value : 1) or not (value : 0)
1958     >
1959     */
1960     public function PaginationIf($attr,$content)
1961     {
1962          $if = array();
1963
1964          if (isset($attr['start'])) {
1965               $sign = (boolean) $attr['start'] ? '' : '!';
1966               $if[] = $sign.'context::PaginationStart()';
1967          }
1968
1969          if (isset($attr['end'])) {
1970               $sign = (boolean) $attr['end'] ? '' : '!';
1971               $if[] = $sign.'context::PaginationEnd()';
1972          }
1973
1974          $this->core->callBehavior('tplIfConditions','PaginationIf',$attr,$content,$if);
1975
1976          if (count($if) != 0) {
1977               return '<?php if('.implode(' && ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
1978          } else {
1979               return $content;
1980          }
1981     }
1982
1983     /*dtd
1984     <!ELEMENT tpl:PaginationURL - O -- link to previoux/next page -->
1985     <!ATTLIST tpl:PaginationURL
1986     offset    CDATA     #IMPLIED  -- page offset (negative for previous pages), default: 0
1987     >
1988     */
1989     public function PaginationURL($attr)
1990     {
1991          $offset = 0;
1992          if (isset($attr['offset'])) {
1993               $offset = (integer) $attr['offset'];
1994          }
1995
1996          $f = $this->getFilters($attr);
1997          return '<?php echo '.sprintf($f,"context::PaginationURL(".$offset.")").'; ?>';
1998     }
1999
2000     /* Comments --------------------------------------- */
2001     /*dtd
2002     <!ELEMENT tpl:Comments - - -- Comments container -->
2003     <!ATTLIST tpl:Comments
2004     with_pings     (0|1)     #IMPLIED  -- include trackbacks in request
2005     lastn     CDATA     #IMPLIED  -- restrict the number of entries
2006     no_context (1|0)         #IMPLIED  -- Override context information
2007     sortby    (title|selected|author|date|id)    #IMPLIED  -- specify comments sort criteria (default : date) (multiple comma-separated sortby can be specified. Use "?asc" or "?desc" as suffix to provide an order for each sorby)
2008     order     (desc|asc)     #IMPLIED  -- result ordering (default: asc)
2009     age       CDATA     #IMPLIED  -- retrieve comments by maximum age (ex: -2 days, last month, last week)
2010     >
2011     */
2012     public function Comments($attr,$content)
2013     {
2014          $p = "";
2015          if (empty($attr['with_pings'])) {
2016               $p .= "\$params['comment_trackback'] = false;\n";
2017          }
2018
2019          $lastn = 0;
2020          if (isset($attr['lastn'])) {
2021               $lastn = abs((integer) $attr['lastn'])+0;
2022          }
2023
2024          if ($lastn > 0) {
2025               $p .= "\$params['limit'] = ".$lastn.";\n";
2026          } else {
2027               $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n";
2028          }
2029
2030          if (empty($attr['no_context']))
2031          {
2032               $p .=
2033               "if (\$_ctx->posts !== null) { ".
2034                    "\$params['post_id'] = \$_ctx->posts->post_id; ".
2035                    "\$core->blog->withoutPassword(false);\n".
2036               "}\n";
2037               $p .=
2038               'if ($_ctx->exists("categories")) { '.
2039                    "\$params['cat_id'] = \$_ctx->categories->cat_id; ".
2040               "}\n";
2041
2042               $p .=
2043               'if ($_ctx->exists("langs")) { '.
2044                    "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ".
2045               "}\n";
2046          }
2047
2048          if (!isset($attr['order'])) {
2049               $attr['order'] = 'asc';
2050          }
2051
2052          $p .= "\$params['order'] = '".$this->getSortByStr($attr,'comment')."';\n";
2053
2054          if (isset($attr['no_content']) && $attr['no_content']) {
2055               $p .= "\$params['no_content'] = true;\n";
2056          }
2057
2058          if (isset($attr['age'])) {
2059               $age = $this->getAge($attr);
2060               $p .= !empty($age) ? "@\$params['sql'] .= ' AND P.post_dt > \'".$age."\'';\n" : '';
2061          }
2062
2063          $res = "<?php\n";
2064          $res .= $this->core->callBehavior("templatePrepareParams",
2065               array("tag" => "Comments","method" => "blog::getComments"),
2066               $attr,$content);
2067          $res .= $p;
2068          $res .= '$_ctx->comments = $core->blog->getComments($params); unset($params);'."\n";
2069          $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n";
2070
2071          if (!empty($attr['with_pings'])) {
2072               $res .= '$_ctx->pings = $_ctx->comments;'."\n";
2073          }
2074
2075          $res .= "?>\n";
2076
2077          $res .=
2078          '<?php while ($_ctx->comments->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->comments = null; ?>';
2079
2080          return $res;
2081     }
2082
2083     /*dtd
2084     <!ELEMENT tpl:CommentAuthor - O -- Comment author -->
2085     */
2086     public function CommentAuthor($attr)
2087     {
2088          $f = $this->getFilters($attr);
2089          return '<?php echo '.sprintf($f,"\$_ctx->comments->comment_author").'; ?>';
2090     }
2091
2092     /*dtd
2093     <!ELEMENT tpl:CommentAuthorDomain - O -- Comment author website domain -->
2094     */
2095     public function CommentAuthorDomain($attr)
2096     {
2097          return '<?php echo preg_replace("#^http(?:s?)://(.+?)/.*$#msu",\'$1\',$_ctx->comments->comment_site); ?>';
2098     }
2099
2100     /*dtd
2101     <!ELEMENT tpl:CommentAuthorLink - O -- Comment author link -->
2102     */
2103     public function CommentAuthorLink($attr)
2104     {
2105          $f = $this->getFilters($attr);
2106          return '<?php echo '.sprintf($f,'$_ctx->comments->getAuthorLink()').'; ?>';
2107     }
2108
2109     /*dtd
2110     <!ELEMENT tpl:CommentAuthorMailMD5 - O -- Comment author email MD5 sum -->
2111     */
2112     public function CommentAuthorMailMD5($attr)
2113     {
2114          return '<?php echo md5($_ctx->comments->comment_email) ; ?>';
2115     }
2116
2117     /*dtd
2118     <!ELEMENT tpl:CommentAuthorURL - O -- Comment author URL -->
2119     */
2120     public function CommentAuthorURL($attr)
2121     {
2122          $f = $this->getFilters($attr);
2123          return '<?php echo '.sprintf($f,'$_ctx->comments->getAuthorURL()').'; ?>';
2124     }
2125
2126     /*dtd
2127     <!ELEMENT tpl:CommentContent - O --  Comment content -->
2128     <!ATTLIST tpl:CommentContent
2129     absolute_urls  (0|1)     #IMPLIED  -- convert URLS to absolute urls
2130     >
2131     */
2132     public function CommentContent($attr)
2133     {
2134          $urls = '0';
2135          if (!empty($attr['absolute_urls'])) {
2136               $urls = '1';
2137          }
2138
2139          $f = $this->getFilters($attr);
2140          return '<?php echo '.sprintf($f,'$_ctx->comments->getContent('.$urls.')').'; ?>';
2141     }
2142
2143     /*dtd
2144     <!ELEMENT tpl:CommentDate - O -- Comment date -->
2145     <!ATTLIST tpl:CommentDate
2146     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
2147     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
2148     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
2149     upddt     CDATA     #IMPLIED  -- if set, uses the comment update time
2150     >
2151     */
2152     public function CommentDate($attr)
2153     {
2154          $format = '';
2155          if (!empty($attr['format'])) {
2156               $format = addslashes($attr['format']);
2157          }
2158
2159          $iso8601 = !empty($attr['iso8601']);
2160          $rfc822 = !empty($attr['rfc822']);
2161          $type = (!empty($attr['upddt']) ? 'upddt' : '');
2162
2163          $f = $this->getFilters($attr);
2164
2165          if ($rfc822) {
2166               return '<?php echo '.sprintf($f,"\$_ctx->comments->getRFC822Date('".$type."')").'; ?>';
2167          } elseif ($iso8601) {
2168               return '<?php echo '.sprintf($f,"\$_ctx->comments->getISO8601Date('".$type."')").'; ?>';
2169          } else {
2170               return '<?php echo '.sprintf($f,"\$_ctx->comments->getDate('".$format."','".$type."')").'; ?>';
2171          }
2172     }
2173
2174     /*dtd
2175     <!ELEMENT tpl:CommentTime - O -- Comment date -->
2176     <!ATTLIST tpl:CommentTime
2177     format    CDATA     #IMPLIED  -- time format
2178     upddt     CDATA     #IMPLIED  -- if set, uses the comment update time
2179     >
2180     */
2181     public function CommentTime($attr)
2182     {
2183          $format = '';
2184          if (!empty($attr['format'])) {
2185               $format = addslashes($attr['format']);
2186          }
2187          $type = (!empty($attr['upddt']) ? 'upddt' : '');
2188
2189          $f = $this->getFilters($attr);
2190          return '<?php echo '.sprintf($f,"\$_ctx->comments->getTime('".$format."','".$type."')").'; ?>';
2191     }
2192
2193     /*dtd
2194     <!ELEMENT tpl:CommentEmail - O -- Comment author email -->
2195     <!ATTLIST tpl:CommentEmail
2196     spam_protected (0|1)     #IMPLIED  -- protect email from spam (default: 1)
2197     >
2198     */
2199     public function CommentEmail($attr)
2200     {
2201          $p = 'true';
2202          if (isset($attr['spam_protected']) && !$attr['spam_protected']) {
2203               $p = 'false';
2204          }
2205
2206          $f = $this->getFilters($attr);
2207          return '<?php echo '.sprintf($f,"\$_ctx->comments->getEmail(".$p.")").'; ?>';
2208     }
2209
2210     /*dtd
2211     <!ELEMENT tpl:CommentEntryTitle - O -- Title of the comment entry -->
2212     */
2213     public function CommentEntryTitle($attr)
2214     {
2215          $f = $this->getFilters($attr);
2216          return '<?php echo '.sprintf($f,'$_ctx->comments->post_title').'; ?>';
2217     }
2218
2219     /*dtd
2220     <!ELEMENT tpl:CommentFeedID - O -- Comment feed ID -->
2221     */
2222     public function CommentFeedID($attr)
2223     {
2224          $f = $this->getFilters($attr);
2225          return '<?php echo '.sprintf($f,'$_ctx->comments->getFeedID()').'; ?>';
2226     }
2227
2228     /*dtd
2229     <!ELEMENT tpl:CommentID - O -- Comment ID -->
2230     */
2231     public function CommentID($attr)
2232     {
2233          return '<?php echo $_ctx->comments->comment_id; ?>';
2234     }
2235
2236     /*dtd
2237     <!ELEMENT tpl:CommentIf - - -- test container for comments -->
2238     <!ATTLIST tpl:CommentIf
2239     is_ping   (0|1)     #IMPLIED  -- test if comment is a trackback (value : 1) or not (value : 0)
2240     >
2241     */
2242     public function CommentIf($attr,$content)
2243     {
2244          $if = array();
2245          $is_ping = null;
2246
2247          if (isset($attr['is_ping'])) {
2248               $sign = (boolean) $attr['is_ping'] ? '' : '!';
2249               $if[] = $sign.'$_ctx->comments->comment_trackback';
2250          }
2251
2252          $this->core->callBehavior('tplIfConditions','CommentIf',$attr,$content,$if);
2253
2254          if (count($if) != 0) {
2255               return '<?php if('.implode(' && ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
2256          } else {
2257               return $content;
2258          }
2259     }
2260
2261     /*dtd
2262     <!ELEMENT tpl:CommentIfFirst - O -- displays value if comment is the first one -->
2263     <!ATTLIST tpl:CommentIfFirst
2264     return    CDATA     #IMPLIED  -- value to display in case of success (default: first)
2265     >
2266     */
2267     public function CommentIfFirst($attr)
2268     {
2269          $ret = isset($attr['return']) ? $attr['return'] : 'first';
2270          $ret = html::escapeHTML($ret);
2271
2272          return
2273          '<?php if ($_ctx->comments->index() == 0) { '.
2274          "echo '".addslashes($ret)."'; } ?>";
2275     }
2276
2277     /*dtd
2278     <!ELEMENT tpl:CommentIfMe - O -- displays value if comment is the from the entry author -->
2279     <!ATTLIST tpl:CommentIfMe
2280     return    CDATA     #IMPLIED  -- value to display in case of success (default: me)
2281     >
2282     */
2283     public function CommentIfMe($attr)
2284     {
2285          $ret = isset($attr['return']) ? $attr['return'] : 'me';
2286          $ret = html::escapeHTML($ret);
2287
2288          return
2289          '<?php if ($_ctx->comments->isMe()) { '.
2290          "echo '".addslashes($ret)."'; } ?>";
2291     }
2292
2293     /*dtd
2294     <!ELEMENT tpl:CommentIfOdd - O -- displays value if comment is  at an odd position -->
2295     <!ATTLIST tpl:CommentIfOdd
2296     return    CDATA     #IMPLIED  -- value to display in case of success (default: odd)
2297     >
2298     */
2299     public function CommentIfOdd($attr)
2300     {
2301          $ret = isset($attr['return']) ? $attr['return'] : 'odd';
2302          $ret = html::escapeHTML($ret);
2303
2304          return
2305          '<?php if (($_ctx->comments->index()+1)%2) { '.
2306          "echo '".addslashes($ret)."'; } ?>";
2307     }
2308
2309     /*dtd
2310     <!ELEMENT tpl:CommentIP - O -- Comment author IP -->
2311     */
2312     public function CommentIP($attr)
2313     {
2314          return '<?php echo $_ctx->comments->comment_ip; ?>';
2315     }
2316
2317     /*dtd
2318     <!ELEMENT tpl:CommentOrderNumber - O -- Comment order in page -->
2319     */
2320     public function CommentOrderNumber($attr)
2321     {
2322          return '<?php echo $_ctx->comments->index()+1; ?>';
2323     }
2324
2325     /*dtd
2326     <!ELEMENT tpl:CommentsFooter - - -- Last comments result container -->
2327     */
2328     public function CommentsFooter($attr,$content)
2329     {
2330          return
2331          "<?php if (\$_ctx->comments->isEnd()) : ?>".
2332          $content.
2333          "<?php endif; ?>";
2334     }
2335
2336     /*dtd
2337     <!ELEMENT tpl:CommentsHeader - - -- First comments result container -->
2338     */
2339     public function CommentsHeader($attr,$content)
2340     {
2341          return
2342          "<?php if (\$_ctx->comments->isStart()) : ?>".
2343          $content.
2344          "<?php endif; ?>";
2345     }
2346
2347     /*dtd
2348     <!ELEMENT tpl:CommentPostURL - O -- Comment Entry URL -->
2349     */
2350     public function CommentPostURL($attr)
2351     {
2352          $f = $this->getFilters($attr);
2353          return '<?php echo '.sprintf($f,'$_ctx->comments->getPostURL()').'; ?>';
2354     }
2355
2356     /*dtd
2357     <!ELEMENT tpl:IfCommentAuthorEmail - - -- Container displayed if comment author email is set -->
2358     */
2359     public function IfCommentAuthorEmail($attr,$content)
2360     {
2361          return
2362          "<?php if (\$_ctx->comments->comment_email) : ?>".
2363          $content.
2364          "<?php endif; ?>";
2365     }
2366
2367     /*dtd
2368     <!ELEMENT tpl:CommentHelp - 0 -- Comment syntax mini help -->
2369     */
2370     public function CommentHelp($attr,$content)
2371     {
2372          return
2373          "<?php if (\$core->blog->settings->system->wiki_comments) {\n".
2374          "  echo __('Comments can be formatted using a simple wiki syntax.');\n".
2375          "} else {\n".
2376          "  echo __('HTML code is displayed as text and web addresses are automatically converted.');\n".
2377          "} ?>";
2378     }
2379
2380     /* Comment preview -------------------------------- */
2381     /*dtd
2382     <!ELEMENT tpl:IfCommentPreview - - -- Container displayed if comment is being previewed -->
2383     */
2384     public function IfCommentPreview($attr,$content)
2385     {
2386          return
2387          '<?php if ($_ctx->comment_preview !== null && $_ctx->comment_preview["preview"]) : ?>'.
2388          $content.
2389          '<?php endif; ?>';
2390     }
2391
2392     /*dtd
2393     <!ELEMENT tpl:CommentPreviewName - O -- Author name for the previewed comment -->
2394     */
2395     public function CommentPreviewName($attr)
2396     {
2397          $f = $this->getFilters($attr);
2398          return '<?php echo '.sprintf($f,'$_ctx->comment_preview["name"]').'; ?>';
2399     }
2400
2401     /*dtd
2402     <!ELEMENT tpl:CommentPreviewEmail - O -- Author email for the previewed comment -->
2403     */
2404     public function CommentPreviewEmail($attr)
2405     {
2406          $f = $this->getFilters($attr);
2407          return '<?php echo '.sprintf($f,'$_ctx->comment_preview["mail"]').'; ?>';
2408     }
2409
2410     /*dtd
2411     <!ELEMENT tpl:CommentPreviewSite - O -- Author site for the previewed comment -->
2412     */
2413     public function CommentPreviewSite($attr)
2414     {
2415          $f = $this->getFilters($attr);
2416          return '<?php echo '.sprintf($f,'$_ctx->comment_preview["site"]').'; ?>';
2417     }
2418
2419     /*dtd
2420     <!ELEMENT tpl:CommentPreviewContent - O -- Content of the previewed comment -->
2421     <!ATTLIST tpl:CommentPreviewContent
2422     raw  (0|1)     #IMPLIED  -- display comment in raw content
2423     >
2424     */
2425     public function CommentPreviewContent($attr)
2426     {
2427          $f = $this->getFilters($attr);
2428
2429          if (!empty($attr['raw'])) {
2430               $co = '$_ctx->comment_preview["rawcontent"]';
2431          } else {
2432               $co = '$_ctx->comment_preview["content"]';
2433          }
2434
2435          return '<?php echo '.sprintf($f,$co).'; ?>';
2436     }
2437
2438     /*dtd
2439     <!ELEMENT tpl:CommentPreviewCheckRemember - O -- checkbox attribute for "remember me" (same value as before preview) -->
2440     */
2441     public function CommentPreviewCheckRemember($attr)
2442     {
2443          return
2444          "<?php if (\$_ctx->comment_preview['remember']) { echo ' checked=\"checked\"'; } ?>";
2445     }
2446
2447     /* Trackbacks ------------------------------------- */
2448     /*dtd
2449     <!ELEMENT tpl:PingBlogName - O -- Trackback blog name -->
2450     */
2451     public function PingBlogName($attr)
2452     {
2453          $f = $this->getFilters($attr);
2454          return '<?php echo '.sprintf($f,'$_ctx->pings->comment_author').'; ?>';
2455     }
2456
2457     /*dtd
2458     <!ELEMENT tpl:PingContent - O -- Trackback content -->
2459     */
2460     public function PingContent($attr)
2461     {
2462          $f = $this->getFilters($attr);
2463          return '<?php echo '.sprintf($f,'$_ctx->pings->getTrackbackContent()').'; ?>';
2464     }
2465
2466     /*dtd
2467     <!ELEMENT tpl:PingDate - O -- Trackback date -->
2468     <!ATTLIST tpl:PingDate
2469     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
2470     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
2471     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
2472     upddt     CDATA     #IMPLIED  -- if set, uses the comment update time
2473     >
2474     */
2475     public function PingDate($attr,$type='')
2476     {
2477          $format = '';
2478          if (!empty($attr['format'])) {
2479               $format = addslashes($attr['format']);
2480          }
2481
2482          $iso8601 = !empty($attr['iso8601']);
2483          $rfc822 = !empty($attr['rfc822']);
2484          $type = (!empty($attr['upddt']) ? 'upddt' : '');
2485
2486          $f = $this->getFilters($attr);
2487
2488          if ($rfc822) {
2489               return '<?php echo '.sprintf($f,"\$_ctx->pings->getRFC822Date('".$type."')").'; ?>';
2490          } elseif ($iso8601) {
2491               return '<?php echo '.sprintf($f,"\$_ctx->pings->getISO8601Date('".$type."')").'; ?>';
2492          } else {
2493               return '<?php echo '.sprintf($f,"\$_ctx->pings->getDate('".$format."','".$type."')").'; ?>';
2494          }
2495     }
2496
2497     /*dtd
2498     <!ELEMENT tpl:PingTime - O -- Trackback date -->
2499     <!ATTLIST tpl:PingTime
2500     format    CDATA     #IMPLIED  -- time format
2501     upddt     CDATA     #IMPLIED  -- if set, uses the comment update time
2502     >
2503     */
2504     public function PingTime($attr)
2505     {
2506          $format = '';
2507          if (!empty($attr['format'])) {
2508               $format = addslashes($attr['format']);
2509          }
2510          $type = (!empty($attr['upddt']) ? 'upddt' : '');
2511
2512          $f = $this->getFilters($attr);
2513          return '<?php echo '.sprintf($f,"\$_ctx->pings->getTime('".$format."','".$type."')").'; ?>';
2514     }
2515
2516     /*dtd
2517     <!ELEMENT tpl:PingEntryTitle - O -- Trackback entry title -->
2518     */
2519     public function PingEntryTitle($attr)
2520     {
2521          $f = $this->getFilters($attr);
2522          return '<?php echo '.sprintf($f,'$_ctx->pings->post_title').'; ?>';
2523     }
2524
2525     /*dtd
2526     <!ELEMENT tpl:PingFeedID - O -- Trackback feed ID -->
2527     */
2528     public function PingFeedID($attr)
2529     {
2530          $f = $this->getFilters($attr);
2531          return '<?php echo '.sprintf($f,'$_ctx->pings->getFeedID()').'; ?>';
2532     }
2533
2534     /*dtd
2535     <!ELEMENT tpl:PingID - O -- Trackback ID -->
2536     */
2537     public function PingID($attr)
2538     {
2539          return '<?php echo $_ctx->pings->comment_id; ?>';
2540     }
2541
2542     /*dtd
2543     <!ELEMENT tpl:PingIfFirst - O -- displays value if trackback is the first one -->
2544     <!ATTLIST tpl:PingIfFirst
2545     return    CDATA     #IMPLIED  -- value to display in case of success (default: first)
2546     >
2547     */
2548     public function PingIfFirst($attr)
2549     {
2550          $ret = isset($attr['return']) ? $attr['return'] : 'first';
2551          $ret = html::escapeHTML($ret);
2552
2553          return
2554          '<?php if ($_ctx->pings->index() == 0) { '.
2555          "echo '".addslashes($ret)."'; } ?>";
2556     }
2557
2558     /*dtd
2559     <!ELEMENT tpl:PingIfOdd - O -- displays value if trackback is  at an odd position -->
2560     <!ATTLIST tpl:PingIfOdd
2561     return    CDATA     #IMPLIED  -- value to display in case of success (default: odd)
2562     >
2563     */
2564     public function PingIfOdd($attr)
2565     {
2566          $ret = isset($attr['return']) ? $attr['return'] : 'odd';
2567          $ret = html::escapeHTML($ret);
2568
2569          return
2570          '<?php if (($_ctx->pings->index()+1)%2) { '.
2571          "echo '".addslashes($ret)."'; } ?>";
2572     }
2573
2574     /*dtd
2575     <!ELEMENT tpl:PingIP - O -- Trackback author IP -->
2576     */
2577     public function PingIP($attr)
2578     {
2579          return '<?php echo $_ctx->pings->comment_ip; ?>';
2580     }
2581
2582     /*dtd
2583     <!ELEMENT tpl:PingNoFollow - O -- displays 'rel="nofollow"' if set in blog -->
2584     */
2585     public function PingNoFollow($attr)
2586     {
2587          return
2588          '<?php if($core->blog->settings->system->comments_nofollow) { '.
2589          'echo \' rel="nofollow"\';'.
2590          '} ?>';
2591     }
2592
2593     /*dtd
2594     <!ELEMENT tpl:PingOrderNumber - O -- Trackback order in page -->
2595     */
2596     public function PingOrderNumber($attr)
2597     {
2598          return '<?php echo $_ctx->pings->index()+1; ?>';
2599     }
2600
2601     /*dtd
2602     <!ELEMENT tpl:PingPostURL - O -- Trackback Entry URL -->
2603     */
2604     public function PingPostURL($attr)
2605     {
2606          $f = $this->getFilters($attr);
2607          return '<?php echo '.sprintf($f,'$_ctx->pings->getPostURL()').'; ?>';
2608     }
2609
2610     /*dtd
2611     <!ELEMENT tpl:Pings - - -- Trackbacks container -->
2612     <!ATTLIST tpl:Pings
2613     with_pings     (0|1)     #IMPLIED  -- include trackbacks in request
2614     lastn     CDATA          #IMPLIED  -- restrict the number of entries
2615     no_context (1|0)         #IMPLIED  -- Override context information
2616     order     (desc|asc)     #IMPLIED  -- result ordering (default: asc)
2617     >
2618     */
2619     public function Pings($attr,$content)
2620     {
2621          $p =
2622          "if (\$_ctx->posts !== null) { ".
2623               "\$params['post_id'] = \$_ctx->posts->post_id; ".
2624               "\$core->blog->withoutPassword(false);\n".
2625          "}\n";
2626
2627          $p .= "\$params['comment_trackback'] = true;\n";
2628
2629          $lastn = 0;
2630          if (isset($attr['lastn'])) {
2631               $lastn = abs((integer) $attr['lastn'])+0;
2632          }
2633
2634          if ($lastn > 0) {
2635               $p .= "\$params['limit'] = ".$lastn.";\n";
2636          } else {
2637               $p .= "if (\$_ctx->nb_comment_per_page !== null) { \$params['limit'] = \$_ctx->nb_comment_per_page; }\n";
2638          }
2639
2640          if (empty($attr['no_context']))
2641          {
2642               $p .=
2643               'if ($_ctx->exists("categories")) { '.
2644                    "\$params['cat_id'] = \$_ctx->categories->cat_id; ".
2645               "}\n";
2646
2647               $p .=
2648               'if ($_ctx->exists("langs")) { '.
2649                    "\$params['sql'] = \"AND P.post_lang = '\".\$core->blog->con->escape(\$_ctx->langs->post_lang).\"' \"; ".
2650               "}\n";
2651          }
2652
2653          $order = 'asc';
2654          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
2655               $order = $attr['order'];
2656          }
2657
2658          $p .= "\$params['order'] = 'comment_dt ".$order."';\n";
2659
2660          if (isset($attr['no_content']) && $attr['no_content']) {
2661               $p .= "\$params['no_content'] = true;\n";
2662          }
2663
2664          $res = "<?php\n";
2665          $res .= $p;
2666          $res .= $this->core->callBehavior("templatePrepareParams",
2667               array("tag" => "Pings","method" => "blog::getComments"),
2668               $attr,$content);
2669          $res .= '$_ctx->pings = $core->blog->getComments($params); unset($params);'."\n";
2670          $res .= "if (\$_ctx->posts !== null) { \$core->blog->withoutPassword(true);}\n";
2671          $res .= "?>\n";
2672
2673          $res .=
2674          '<?php while ($_ctx->pings->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->pings = null; ?>';
2675
2676          return $res;
2677     }
2678
2679     /*dtd
2680     <!ELEMENT tpl:PingsFooter - - -- Last trackbacks result container -->
2681     */
2682     public function PingsFooter($attr,$content)
2683     {
2684          return
2685          "<?php if (\$_ctx->pings->isEnd()) : ?>".
2686          $content.
2687          "<?php endif; ?>";
2688     }
2689
2690     /*dtd
2691     <!ELEMENT tpl:PingsHeader - - -- First trackbacks result container -->
2692     */
2693     public function PingsHeader($attr,$content)
2694     {
2695          return
2696          "<?php if (\$_ctx->pings->isStart()) : ?>".
2697          $content.
2698          "<?php endif; ?>";
2699     }
2700
2701     /*dtd
2702     <!ELEMENT tpl:PingTitle - O -- Trackback title -->
2703     */
2704     public function PingTitle($attr)
2705     {
2706          $f = $this->getFilters($attr);
2707          return '<?php echo '.sprintf($f,'$_ctx->pings->getTrackbackTitle()').'; ?>';
2708     }
2709
2710     /*dtd
2711     <!ELEMENT tpl:PingAuthorURL - O -- Trackback author URL -->
2712     */
2713     public function PingAuthorURL($attr)
2714     {
2715          $f = $this->getFilters($attr);
2716          return '<?php echo '.sprintf($f,'$_ctx->pings->getAuthorURL()').'; ?>';
2717     }
2718
2719     # System
2720     /*dtd
2721     <!ELEMENT tpl:SysBehavior - O -- Call a given behavior -->
2722     <!ATTLIST tpl:SysBehavior
2723     behavior  CDATA     #IMPLIED  -- behavior to call
2724     >
2725     */
2726     public function SysBehavior($attr,$raw)
2727     {
2728          if (!isset($attr['behavior'])) {
2729               return;
2730          }
2731
2732          $b = addslashes($attr['behavior']);
2733          return
2734          '<?php if ($core->hasBehavior(\''.$b.'\')) { '.
2735               '$core->callBehavior(\''.$b.'\',$core,$_ctx);'.
2736          '} ?>';
2737     }
2738
2739     /*dtd
2740     <!ELEMENT tpl:SysIf - - -- System settings tester container -->
2741     <!ATTLIST tpl:SysIf
2742     categories          (0|1)     #IMPLIED  -- test if categories are set in current context (value : 1) or not (value : 0)
2743     posts               (0|1)     #IMPLIED  -- test if posts are set in current context (value : 1) or not (value : 0)
2744     blog_lang           CDATA     #IMPLIED  -- tests if blog language is the one given in parameter
2745     current_tpl         CDATA     #IMPLIED  -- tests if current template is the one given in paramater
2746     current_mode        CDATA     #IMPLIED  -- tests if current URL mode is the one given in parameter
2747     has_tpl             CDATA     #IMPLIED  -- tests if a named template exists
2748     has_tag             CDATA     #IMPLIED  -- tests if a named template block or value exists
2749     blog_id             CDATA     #IMPLIED  -- tests if current blog ID is the one given in parameter
2750     comments_active     (0|1)     #IMPLIED  -- test if comments are enabled blog-wide
2751     pings_active        (0|1)     #IMPLIED  -- test if trackbacks are enabled blog-wide
2752     wiki_comments       (0|1)     #IMPLIED  -- test if wiki syntax is enabled for comments
2753     operator            (and|or)  #IMPLIED  -- combination of conditions, if more than 1 specifiec (default: and)
2754     >
2755     */
2756     public function SysIf($attr,$content)
2757     {
2758          $if = new ArrayObject();
2759          $is_ping = null;
2760
2761          $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
2762
2763          if (isset($attr['categories'])) {
2764               $sign = (boolean) $attr['categories'] ? '!' : '=';
2765               $if[] = '$_ctx->categories '.$sign.'== null';
2766          }
2767
2768          if (isset($attr['posts'])) {
2769               $sign = (boolean) $attr['posts'] ? '!' : '=';
2770               $if[] = '$_ctx->posts '.$sign.'== null';
2771          }
2772
2773          if (isset($attr['blog_lang'])) {
2774               $if[] = "\$core->blog->settings->system->lang == '".addslashes($attr['blog_lang'])."'";
2775          }
2776
2777          if (isset($attr['current_tpl'])) {
2778               $sign = '=';
2779               if (substr($attr['current_tpl'],0,1) == '!') {
2780                    $sign = '!';
2781                    $attr['current_tpl'] = substr($attr['current_tpl'],1);
2782               }
2783               $if[] = "\$_ctx->current_tpl ".$sign."= '".addslashes($attr['current_tpl'])."'";
2784          }
2785
2786          if (isset($attr['current_mode'])) {
2787               $sign = '=';
2788               if (substr($attr['current_mode'],0,1) == '!') {
2789                    $sign = '!';
2790                    $attr['current_mode'] = substr($attr['current_mode'],1);
2791               }
2792               $if[] = "\$core->url->type ".$sign."= '".addslashes($attr['current_mode'])."'";
2793          }
2794
2795          if (isset($attr['has_tpl'])) {
2796               $sign = '';
2797               if (substr($attr['has_tpl'],0,1) == '!') {
2798                    $sign = '!';
2799                    $attr['has_tpl'] = substr($attr['has_tpl'],1);
2800               }
2801               $if[] = $sign."\$core->tpl->getFilePath('".addslashes($attr['has_tpl'])."') !== false";
2802          }
2803
2804          if (isset($attr['has_tag'])) {
2805               $sign = 'true';
2806               if (substr($attr['has_tag'],0,1) == '!') {
2807                    $sign = 'false';
2808                    $attr['has_tag'] = substr($attr['has_tag'],1);
2809               }
2810               $if[] =  "\$core->tpl->tagExists('".addslashes($attr['has_tag'])."') === ".$sign;
2811          }
2812
2813          if (isset($attr['blog_id'])) {
2814               $sign = '';
2815               if (substr($attr['blog_id'],0,1) == '!') {
2816                    $sign = '!';
2817                    $attr['blog_id'] = substr($attr['blog_id'],1);
2818               }
2819               $if[] = $sign."(\$core->blog->id == '".addslashes($attr['blog_id'])."')";
2820          }
2821
2822          if (isset($attr['comments_active'])) {
2823               $sign = (boolean) $attr['comments_active'] ? '' : '!';
2824               $if[] = $sign.'$core->blog->settings->system->allow_comments';
2825          }
2826
2827          if (isset($attr['pings_active'])) {
2828               $sign = (boolean) $attr['pings_active'] ? '' : '!';
2829               $if[] = $sign.'$core->blog->settings->system->allow_trackbacks';
2830          }
2831
2832          if (isset($attr['wiki_comments'])) {
2833               $sign = (boolean) $attr['wiki_comments'] ? '' : '!';
2834               $if[] = $sign.'$core->blog->settings->system->wiki_comments';
2835          }
2836
2837          if (isset($attr['search_count']) &&
2838               preg_match('/^((=|!|&gt;|&lt;)=|(&gt;|&lt;))\s*[0-9]+$/',trim($attr['search_count']))) {
2839               $if[] = '(isset($_search_count) && $_search_count '.html::decodeEntities($attr['search_count']).')';
2840          }
2841
2842          $this->core->callBehavior('tplIfConditions','SysIf',$attr,$content,$if);
2843
2844          if (count($if) != 0) {
2845               return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
2846          } else {
2847               return $content;
2848          }
2849     }
2850
2851     /*dtd
2852     <!ELEMENT tpl:SysIfCommentPublished - - -- Container displayed if comment has been published -->
2853     */
2854     public function SysIfCommentPublished($attr,$content)
2855     {
2856          return
2857          '<?php if (!empty($_GET[\'pub\'])) : ?>'.
2858          $content.
2859          '<?php endif; ?>';
2860     }
2861
2862     /*dtd
2863     <!ELEMENT tpl:SysIfCommentPending - - -- Container displayed if comment is pending after submission -->
2864     */
2865     public function SysIfCommentPending($attr,$content)
2866     {
2867          return
2868          '<?php if (isset($_GET[\'pub\']) && $_GET[\'pub\'] == 0) : ?>'.
2869          $content.
2870          '<?php endif; ?>';
2871     }
2872
2873     /*dtd
2874     <!ELEMENT tpl:SysFeedSubtitle - O -- Feed subtitle -->
2875     */
2876     public function SysFeedSubtitle($attr)
2877     {
2878          $f = $this->getFilters($attr);
2879          return '<?php if ($_ctx->feed_subtitle !== null) { echo '.sprintf($f,'$_ctx->feed_subtitle').';} ?>';
2880     }
2881
2882     /*dtd
2883     <!ELEMENT tpl:SysIfFormError - O -- Container displayed if an error has been detected after form submission -->
2884     */
2885     public function SysIfFormError($attr,$content)
2886     {
2887          return
2888          '<?php if ($_ctx->form_error !== null) : ?>'.
2889          $content.
2890          '<?php endif; ?>';
2891     }
2892
2893     /*dtd
2894     <!ELEMENT tpl:SysFormError - O -- Form error -->
2895     */
2896     public function SysFormError($attr)
2897     {
2898          return
2899          '<?php if ($_ctx->form_error !== null) { echo $_ctx->form_error; } ?>';
2900     }
2901
2902     public function SysPoweredBy($attr)
2903     {
2904          return
2905          '<?php printf(__("Powered by %s"),"<a href=\"http://dotclear.org/\">Dotclear</a>"); ?>';
2906     }
2907
2908     public function SysSearchString($attr)
2909     {
2910          $s = isset($attr['string']) ? $attr['string'] : '%1$s';
2911
2912          $f = $this->getFilters($attr);
2913          return '<?php if (isset($_search)) { echo sprintf(__(\''.$s.'\'),'.sprintf($f,'$_search').',$_search_count);} ?>';
2914     }
2915
2916     public function SysSelfURI($attr)
2917     {
2918          $f = $this->getFilters($attr);
2919          return '<?php echo '.sprintf($f,'http::getSelfURI()').'; ?>';
2920     }
2921
2922     /*dtd
2923     <!ELEMENT tpl:else - O -- else: statement -->
2924     */
2925     public function GenericElse($attr)
2926     {
2927          return '<?php else: ?>';
2928     }
2929}
2930
2931# Template nodes, for parsing purposes
2932
2933# Generic list node, this one may only be instanciated
2934# once for root element
2935class tplNode
2936{
2937     # Basic tree structure : links to parent, children forrest
2938     protected $parentNode;
2939     protected $children;
2940
2941     public function __construct() {
2942          $this->children = array();
2943          $this->parentNode = null;
2944     }
2945
2946     // Returns compiled block
2947     public function compile($tpl) {
2948          $res='';
2949          foreach ($this->children as $child) {
2950               $res .= $child->compile($tpl);
2951          }
2952          return $res;
2953     }
2954
2955     # Add a children to current node
2956     public function addChild ($child) {
2957          $this->children[] = $child;
2958          $child->setParent($this);
2959     }
2960
2961     # Defines parent for current node
2962     protected function setParent($parent) {
2963          $this->parentNode = $parent;
2964     }
2965
2966     # Retrieves current node parent.
2967     # If parent is root node, null is returned
2968     public function getParent() {
2969          return $this->parentNode;
2970     }
2971
2972     # Current node tag
2973     public function getTag() {
2974          return "ROOT";
2975     }
2976}
2977
2978// Text node, for any non-tpl content
2979class tplNodeText extends tplNode
2980{
2981     // Simple text node, only holds its content
2982     protected $content;
2983
2984     public function __construct($text) {
2985          parent::__construct();
2986          $this->content=$text;
2987     }
2988
2989     public function compile($tpl) {
2990          return $this->content;
2991     }
2992
2993     public function getTag() {
2994          return "TEXT";
2995     }
2996}
2997
2998// Block node, for all <tpl:Tag>...</tpl:Tag>
2999class tplNodeBlock extends tplNode
3000{
3001     protected $attr;
3002     protected $tag;
3003     protected $closed;
3004
3005     public function __construct($tag,$attr) {
3006          parent::__construct();
3007          $this->content='';
3008          $this->tag = $tag;
3009          $this->attr = $attr;
3010          $this->closed=false;
3011     }
3012     public function setClosing() {
3013          $this->closed = true;
3014     }
3015     public function isClosed() {
3016          return $this->closed;
3017     }
3018     public function compile($tpl) {
3019          if ($this->closed) {
3020               $content = parent::compile($tpl);
3021               return $tpl->compileBlockNode($this->tag,$this->attr,$content);
3022          } else {
3023               // if tag has not been closed, silently ignore its content...
3024               return '';
3025          }
3026     }
3027     public function getTag() {
3028          return $this->tag;
3029     }
3030}
3031
3032// Value node, for all {{tpl:Tag}}
3033class tplNodeValue extends tplNode
3034{
3035     protected $attr;
3036     protected $str_attr;
3037     protected $tag;
3038
3039     public function __construct($tag,$attr,$str_attr) {
3040          parent::__construct();
3041          $this->content='';
3042          $this->tag = $tag;
3043          $this->attr = $attr;
3044          $this->str_attr = $str_attr;
3045     }
3046
3047     public function compile($tpl) {
3048          return $tpl->compileValueNode($this->tag,$this->attr,$this->str_attr);
3049     }
3050
3051     public function getTag() {
3052          return $this->tag;
3053     }
3054}
Note: See TracBrowser for help on using the repository browser.

Sites map