Dotclear

source: inc/public/class.dc.template.php @ 1674:00146501e490

Revision 1674:00146501e490, 92.8 KB checked in by Florent Cotton <florent.cotton@…>, 11 years ago (diff)

Support des pingbacks : après le support de la détection et de l'envoi de pingbacks, au tour du support en réception.
Dans le détail (ou presque) :

  • Ajout d'un endpoint "pingback.ping" XML-RPC, mais le tout le gros du traitement est dans le fichier class.dc.trackback.php
  • Ajout d'une méthode "receive_pb" dans la classe dcTrackback pour la prise en charge quasi-complète de la réception et enregistrement d'un pingback.
  • Ajout d'une balise template {{tpl:BlogXMLRPCURL}} pour retourner l'URL du serveur XML-RPC du blog courant
  • Ajout d'un bloc au niveau des en-têtes dans les templates par défaut post.html et page.html pour la mise en oeuvre si besoin d'un <link rel="pingback" ../>
  • Ajout de l'envoi d'un en-tête HTTP supplémentaire "X-Pingback" dans les gestionnaires d'URLs pour les types "post" et "pages"

Reste plus qu'à tester en conditions réelles et à polir au besoin.

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

Sites map