Dotclear

source: inc/public/class.dc.template.php @ 2864:a809679a5a42

Revision 2864:a809679a5a42, 91.1 KB checked in by franck <carnet.franck.paul@…>, 11 years ago (diff)

Pagination should take into account the lastn parameter specified in tpl:Entries tag if present

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

Sites map