Dotclear

source: inc/public/class.dc.template.php @ 1249:3d1fd7774621

Revision 1249:3d1fd7774621, 92.8 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

New template tpl:BlogParentThemeURL (return URL of parent theme of blog's theme if any, URL of blog's theme otherwise)

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

Sites map