Dotclear

source: inc/public/class.dc.template.php @ 351:a10315556ae4

Revision 351:a10315556ae4, 95.6 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

Fixed a bug in template compiler : now tests whether latest block has not been properly closed.

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

Sites map