Dotclear

source: inc/public/class.dc.template.php @ 912:18eae8f3a180

Revision 912:18eae8f3a180, 91.3 KB checked in by franck <carnet.franck.paul@…>, 13 years ago (diff)

Add "republished" test attribute for EntryIf? tag and used in atom.xml. May be useful for theme creators in order to add a mention if a post has been modified since his publication. Fixes #1161.

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

Sites map