Dotclear

source: inc/public/class.dc.template.php @ 2577:9a2b43a68e3d

Revision 2577:9a2b43a68e3d, 88.9 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

Number of entries on first home page may be set (does not modify number of entries on other pages), fixes #1269

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

Sites map