Dotclear

source: inc/public/class.dc.template.php @ 1610:2d5ebb2fcdfd

Revision 1610:2d5ebb2fcdfd, 92.5 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

Fixes #472, <tpl:Categories> (new without_empty="0" attribute) and categories' widget ("Include empty categories" checkbox) may includes empty categories.

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

Sites map