Dotclear

source: inc/public/class.dc.template.php @ 407:eb5bd0f66932

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

Sites map