Dotclear

source: inc/public/class.dc.template.php @ 929:a52872aaada9

Revision 929:a52872aaada9, 91.9 KB checked in by franck <carnet.franck.paul@…>, 13 years ago (diff)

Add tpl:else tag, fixes #855

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

Sites map