Dotclear

source: inc/public/class.dc.template.php @ 851:0993f64c4809

Revision 851:0993f64c4809, 52.6 KB checked in by Dsls <dsls@…>, 13 years ago (diff)

sexy step 2 : no more cats.

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          # l10n
35          $this->addValue('lang',array($this,'l10n'));
36         
37          # Loops test tags
38          $this->addBlock('LoopPosition',array($this,'LoopPosition'));
39         
40          # Archives
41          $this->addBlock('Archives',array($this,'Archives'));
42          $this->addBlock('ArchivesHeader',array($this,'ArchivesHeader'));
43          $this->addBlock('ArchivesFooter',array($this,'ArchivesFooter'));
44          $this->addBlock('ArchivesYearHeader',array($this,'ArchivesYearHeader'));
45          $this->addBlock('ArchivesYearFooter',array($this,'ArchivesYearFooter'));
46          $this->addValue('ArchiveDate',array($this,'ArchiveDate'));
47          $this->addBlock('ArchiveNext',array($this,'ArchiveNext'));
48          $this->addBlock('ArchivePrevious',array($this,'ArchivePrevious'));
49          $this->addValue('ArchiveEntriesCount',array($this,'ArchiveEntriesCount'));
50          $this->addValue('ArchiveURL',array($this,'ArchiveURL'));
51         
52          # Blog
53          $this->addValue('BlogArchiveURL',array($this,'BlogArchiveURL'));
54          $this->addValue('BlogCopyrightNotice',array($this,'BlogCopyrightNotice'));
55          $this->addValue('BlogDescription',array($this,'BlogDescription'));
56          $this->addValue('BlogEditor',array($this,'BlogEditor'));
57          $this->addValue('BlogFeedID',array($this,'BlogFeedID'));
58          $this->addValue('BlogFeedURL',array($this,'BlogFeedURL'));
59          $this->addValue('BlogRSDURL',array($this,'BlogRSDURL'));
60          $this->addValue('BlogName',array($this,'BlogName'));
61          $this->addValue('BlogLanguage',array($this,'BlogLanguage'));
62          $this->addValue('BlogThemeURL',array($this,'BlogThemeURL'));
63          $this->addValue('BlogUpdateDate',array($this,'BlogUpdateDate'));
64          $this->addValue('BlogID',array($this,'BlogID'));
65          $this->addValue('BlogURL',array($this,'BlogURL'));
66          $this->addValue('BlogPublicURL',array($this,'BlogPublicURL'));
67          $this->addValue('BlogQmarkURL',array($this,'BlogQmarkURL'));
68          $this->addValue('BlogMetaRobots',array($this,'BlogMetaRobots'));
69         
70          # Entries
71          $this->addBlock('DateFooter',array($this,'DateFooter'));
72          $this->addBlock('DateHeader',array($this,'DateHeader'));
73          $this->addBlock('Entries',array($this,'Entries'));
74          $this->addBlock('EntriesFooter',array($this,'EntriesFooter'));
75          $this->addBlock('EntriesHeader',array($this,'EntriesHeader'));
76          $this->addValue('EntryExcerpt',array($this,'EntryExcerpt'));
77          $this->addValue('EntryAuthorCommonName',array($this,'EntryAuthorCommonName'));
78          $this->addValue('EntryAuthorDisplayName',array($this,'EntryAuthorDisplayName'));
79          $this->addValue('EntryAuthorEmail',array($this,'EntryAuthorEmail'));
80          $this->addValue('EntryAuthorID',array($this,'EntryAuthorID'));
81          $this->addValue('EntryAuthorLink',array($this,'EntryAuthorLink'));
82          $this->addValue('EntryAuthorURL',array($this,'EntryAuthorURL'));
83          $this->addValue('EntryBasename',array($this,'EntryBasename'));
84          $this->addValue('EntryContent',array($this,'EntryContent'));
85          $this->addValue('EntryDate',array($this,'EntryDate'));
86          $this->addValue('EntryFeedID',array($this,'EntryFeedID'));
87          $this->addValue('EntryFirstImage',array($this,'EntryFirstImage'));
88          $this->addValue('EntryID',array($this,'EntryID'));
89          $this->addBlock('EntryIf',array($this,'EntryIf'));
90          $this->addValue('EntryIfFirst',array($this,'EntryIfFirst'));
91          $this->addValue('EntryIfOdd',array($this,'EntryIfOdd'));
92          $this->addValue('EntryIfSelected',array($this,'EntryIfSelected'));
93          $this->addValue('EntryLang',array($this,'EntryLang'));
94          $this->addBlock('EntryNext',array($this,'EntryNext'));
95          $this->addBlock('EntryPrevious',array($this,'EntryPrevious'));
96          $this->addValue('EntryTitle',array($this,'EntryTitle'));
97          $this->addValue('EntryTime',array($this,'EntryTime'));
98          $this->addValue('EntryURL',array($this,'EntryURL'));
99         
100          # Languages
101          $this->addBlock('Languages',array($this,'Languages'));
102          $this->addBlock('LanguagesHeader',array($this,'LanguagesHeader'));
103          $this->addBlock('LanguagesFooter',array($this,'LanguagesFooter'));
104          $this->addValue('LanguageCode',array($this,'LanguageCode'));
105          $this->addBlock('LanguageIfCurrent',array($this,'LanguageIfCurrent'));
106          $this->addValue('LanguageURL',array($this,'LanguageURL'));
107         
108          # Pagination
109          $this->addBlock('Pagination',array($this,'Pagination'));
110          $this->addValue('PaginationCounter',array($this,'PaginationCounter'));
111          $this->addValue('PaginationCurrent',array($this,'PaginationCurrent'));
112          $this->addBlock('PaginationIf',array($this,'PaginationIf'));
113          $this->addValue('PaginationURL',array($this,'PaginationURL'));
114         
115          # System
116          $this->addValue('SysBehavior',array($this,'SysBehavior'));
117          $this->addBlock('SysIf',array($this,'SysIf'));
118          $this->addBlock('SysIfFormError',array($this,'SysIfFormError'));
119          $this->addValue('SysFeedSubtitle',array($this,'SysFeedSubtitle'));
120          $this->addValue('SysFormError',array($this,'SysFormError'));
121          $this->addValue('SysPoweredBy',array($this,'SysPoweredBy'));
122          $this->addValue('SysSearchString',array($this,'SysSearchString'));
123          $this->addValue('SysSelfURI',array($this,'SysSelfURI'));
124     }
125     
126     public function getData($________)
127     {
128          # --BEHAVIOR-- tplBeforeData
129          if ($this->core->hasBehavior('tplBeforeData'))
130          {
131               self::$_r = $this->core->callBehavior('tplBeforeData',$this->core);
132               if (self::$_r) {
133                    return self::$_r;
134               }
135          }
136         
137          parent::getData($________);
138         
139          # --BEHAVIOR-- tplAfterData
140          if ($this->core->hasBehavior('tplAfterData')) {
141               $this->core->callBehavior('tplAfterData',$this->core,self::$_r);
142          }
143         
144          return self::$_r;
145     }
146     
147     protected function compileFile($file)
148     {
149          $fc = file_get_contents($file);
150         
151          $this->compile_stack[] = $file;
152         
153          # Remove every PHP tags
154          if ($this->remove_php)
155          {
156               $fc = preg_replace('/<\?(?=php|=|\s).*?\?>/ms','',$fc);
157          }
158         
159          # Transform what could be considered as PHP short tags
160          $fc = preg_replace('/(<\?(?!php|=|\s))(.*?)(\?>)/ms',
161          '<?php echo "$1"; ?>$2<?php echo "$3"; ?>',$fc);
162         
163          # Remove template comments <!-- #... -->
164          $fc = preg_replace('/(^\s*)?<!-- #(.*?)-->/ms','',$fc);
165         
166          # Lexer part : split file into small pieces
167          # each array entry will be either a tag or plain text
168          $blocks = preg_split(
169               '#(<tpl:\w+[^>]*>)|(</tpl:\w+>)|({{tpl:\w+[^}]*}})#msu',$fc,-1,
170               PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
171         
172          # Next : build semantic tree from tokens.
173          $rootNode = new tplNode();
174          $node = $rootNode;
175          $errors = array();
176          foreach ($blocks as $id => $block) {
177               $isblock = preg_match('#<tpl:(\w+)(?:(\s+.*?)>|>)|</tpl:(\w+)>|{{tpl:(\w+)(\s(.*?))?}}#ms',$block,$match);
178               if ($isblock == 1) {
179                    if (substr($match[0],1,1) == '/') {
180                         // Closing tag, check if it matches current opened node
181                         $tag = $match[3];
182                         if (($node instanceof tplNodeBlock) && $node->getTag() == $tag) {
183                              $node->setClosing();
184                              $node = $node->getParent();
185                         } else {
186                              // Closing tag does not match opening tag
187                              // Search if it closes a parent tag
188                              $search = $node;
189                              while($search->getTag() != 'ROOT' && $search->getTag() != $tag) {
190                                   $search = $search->getParent();
191                              }
192                              if ($search->getTag() == $tag) {
193                                   $errors[] = sprintf(
194                                        __('Did not find closing tag for block <tpl:%s>. Content has been ignored.'),
195                                        html::escapeHTML($node->getTag()));
196                                   $search->setClosing();
197                                   $node = $search->getParent();
198                              } else {
199                                   $errors[]=sprintf(
200                                        __('Unexpected closing tag </tpl:%s> found.'),
201                                        $tag);;
202                              }
203                         }
204                    } elseif (substr($match[0],0,1) == '{') {
205                         // Value tag
206                         $tag = $match[4];
207                         $str_attr = '';
208                         $attr = array();
209                         if (isset($match[6])) {
210                              $str_attr = $match[6];
211                              $attr = $this->getAttrs($match[6]);
212                         }
213                         $node->addChild(new tplNodeValue($tag,$attr,$str_attr));
214                    } else {
215                         // Opening tag, create new node and dive into it
216                         $tag = $match[1];
217                         $newnode = new tplNodeBlock($tag,isset($match[2])?$this->getAttrs($match[2]):array());
218                         $node->addChild($newnode);
219                         $node = $newnode;
220                    }
221               } else {
222                    // Simple text
223                    $node->addChild(new tplNodeText($block));
224               }
225          }
226         
227          if (($node instanceof tplNodeBlock) && !$node->isClosed()) {
228               $errors[] = sprintf(
229                    __('Did not find closing tag for block <tpl:%s>. Content has been ignored.'),
230                    html::escapeHTML($node->getTag()));
231          }
232         
233          $err = "";
234          if (count($errors) > 0) {
235               $err = "\n\n<!-- \n".
236                    __('WARNING: the following errors have been found while parsing template file :').
237                    "\n * ".
238                    join("\n * ",$errors).
239                    "\n -->\n";
240          }
241         
242          return $rootNode->compile($this).$err;
243     }
244   
245     public function compileBlockNode($tag,$attr,$content)
246     {
247          $this->current_tag = $tag;
248          $attr = new ArrayObject($attr);
249          # --BEHAVIOR-- templateBeforeBlock
250          $res = $this->core->callBehavior('templateBeforeBlock',$this->core,$this->current_tag,$attr);
251         
252          # --BEHAVIOR-- templateInsideBlock
253          $this->core->callBehavior('templateInsideBlock',$this->core,$this->current_tag,$attr,array(&$content));
254         
255          if (isset($this->blocks[$this->current_tag])) {
256               $res .= call_user_func($this->blocks[$this->current_tag],$attr,$content);
257          } elseif ($this->unknown_block_handler != null) {
258               $res .= call_user_func($this->unknown_block_handler,$this->current_tag,$attr,$content);
259          }
260         
261          # --BEHAVIOR-- templateAfterBlock
262          $res .= $this->core->callBehavior('templateAfterBlock',$this->core,$this->current_tag,$attr);
263         
264          return $res;
265     }
266     
267     public function compileValueNode($tag,$attr,$str_attr)
268     {
269          $this->current_tag = $tag;
270         
271          $attr = new ArrayObject($attr);
272          # --BEHAVIOR-- templateBeforeValue
273          $res = $this->core->callBehavior('templateBeforeValue',$this->core,$this->current_tag,$attr);
274         
275          if (isset($this->values[$this->current_tag])) {
276               $res .= call_user_func($this->values[$this->current_tag],$attr,ltrim($str_attr));
277          } elseif ($this->unknown_value_handler != null) {
278               $res .= call_user_func($this->unknown_value_handler,$this->current_tag,$attr,$str_attr);
279          }
280       
281          # --BEHAVIOR-- templateAfterValue
282          $res .= $this->core->callBehavior('templateAfterValue',$this->core,$this->current_tag,$attr);
283         
284          return $res;
285     }
286   
287     public function setUnknownValueHandler($callback)
288     {
289          if (is_callable($callback)) {
290               $this->unknown_value_handler = $callback;
291          }
292     }
293   
294     public function setUnknownBlockHandler($callback)
295     {
296          if (is_callable($callback)) {
297               $this->unknown_block_handler = $callback;
298          }
299     }
300   
301     public function getFilters($attr)
302     {
303          $p[0] = '0';   # encode_xml
304          $p[1] = '0';   # remove_html
305          $p[2] = '0';   # cut_string
306          $p[3] = '0';   # lower_case
307          $p[4] = '0';   # upper_case
308         
309          $p[0] = (integer) (!empty($attr['encode_xml']) || !empty($attr['encode_html']));
310          $p[1] = (integer) !empty($attr['remove_html']);
311         
312          if (!empty($attr['cut_string']) && (integer) $attr['cut_string'] > 0) {
313               $p[2] = (integer) $attr['cut_string'];
314          }
315         
316          $p[3] = (integer) !empty($attr['lower_case']);
317          $p[4] = (integer) !empty($attr['upper_case']);
318         
319          return "context::global_filter(%s,".implode(",",$p).",'".addslashes($this->current_tag)."')";
320     }
321     
322     public static function getOperator($op)
323     {
324          switch (strtolower($op))
325          {
326               case 'or':
327               case '||':
328                    return '||';
329               case 'and':
330               case '&&':
331               default:
332                    return '&&';
333          }
334     }
335     
336     public function getSortByStr($attr,$table = null)
337     {
338          $res = array();
339         
340          $default_order = 'desc';
341         
342          $default_alias = array(
343               'post' => array(
344                    'title' => 'post_title',
345                    'selected' => 'post_selected',
346                    'author' => 'user_id',
347                    'date' => 'post_dt',
348                    'id' => 'post_id',
349               )
350          );
351         
352          $alias = new ArrayObject();
353         
354          # --BEHAVIOR-- templateCustomSortByAlias
355          $this->core->callBehavior('templateCustomSortByAlias',$alias);
356         
357          $alias = $alias->getArrayCopy();
358         
359          if (is_array($alias)) {
360               foreach ($alias as $k => $v) {
361                    if (!is_array($v)) {
362                         $alias[$k] = array();
363                    }
364                    if (!is_array($v)) {
365                         $default_alias[$k] = array();
366                    }
367                    $default_alias[$k] = array_merge($default_alias[$k],$alias[$k]);
368               }
369          }
370         
371          if (!array_key_exists($table,$default_alias)) {
372               return implode(', ',$res);
373          }
374         
375          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
376               $default_order = $attr['order'];
377          }
378          if (isset($attr['sortby'])) {
379               $sorts = explode(',',$attr['sortby']);
380               foreach ($sorts as $k => $sort) {
381                    $order = $default_order;
382                    if (preg_match('/([a-z]*)\s*\?(desc|asc)$/i',$sort,$matches)) {
383                         $sort = $matches[1];
384                         $order = $matches[2];
385                    }
386                    if (array_key_exists($sort,$default_alias[$table])) {
387                         array_push($res,$default_alias[$table][$sort].' '.$order);
388                    }
389               }
390          }
391         
392          if (count($res) === 0) {
393               array_push($res,$default_alias[$table]['date'].' '.$default_order);
394          }
395         
396          return implode(', ',$res);
397     }
398     
399     public function getAge($attr)
400     {
401          if (isset($attr['age']) && preg_match('/^(\-[0-9]+|last).*$/i',$attr['age'])) {
402               if (($ts = strtotime($attr['age'])) !== false) {
403                    return dt::str('%Y-%m-%d %H:%m:%S',$ts);
404               }
405          }
406          return '';
407     }
408     
409     /* TEMPLATE FUNCTIONS
410     ------------------------------------------------------- */
411     
412     public function l10n($attr,$str_attr)
413     {
414          # Normalize content
415          $str_attr = preg_replace('/\s+/x',' ',$str_attr);
416         
417          return "<?php echo __('".str_replace("'","\\'",$str_attr)."'); ?>";
418     }
419     
420     public function LoopPosition($attr,$content)
421     {
422          $start = isset($attr['start']) ? (integer) $attr['start'] : '0';
423          $length = isset($attr['length']) ? (integer) $attr['length'] : 'null';
424          $even = isset($attr['even']) ? (integer) (boolean) $attr['even'] : 'null';
425         
426          if ($start > 0) {
427               $start--;
428          }
429         
430          return
431          '<?php if ($_ctx->loopPosition('.$start.','.$length.','.$even.')) : ?>'.
432          $content.
433          "<?php endif; ?>";
434     }
435     
436     
437     /* Archives ------------------------------------------- */
438     /*dtd
439     <!ELEMENT tpl:Archives - - -- Archives dates loop -->
440     <!ATTLIST tpl:Archives
441     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
442     no_context (1|0)              #IMPLIED  -- Override context information
443     order     (asc|desc)          #IMPLIED  -- Sort asc or desc --
444     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
445     post_lang CDATA          #IMPLIED  -- Filter on the given language
446     >
447     */
448     public function Archives($attr,$content)
449     {
450          $p = "if (!isset(\$params)) \$params = array();\n";
451          $p .= "\$params['type'] = 'month';\n";
452          if (isset($attr['type'])) {
453               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
454          }
455         
456          if (isset($attr['post_type'])) {
457               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
458          }
459       
460          if (isset($attr['post_lang'])) {
461               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
462          }
463         
464          $order = 'desc';
465          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
466               $p .= "\$params['order'] = '".$attr['order']."';\n ";
467          }
468         
469          $res = "<?php\n";
470          $res .= $p;
471          $res .= $this->core->callBehavior("templatePrepareParams", 
472               array("tag" => "Archives","method" => "blog::getDates"), 
473               $attr,$content);
474          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
475          $res .= "?>\n";
476         
477          $res .=
478          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
479         
480          return $res;
481     }
482     
483     /*dtd
484     <!ELEMENT tpl:ArchivesHeader - - -- First archives result container -->
485     */
486     public function ArchivesHeader($attr,$content)
487     {
488          return
489          "<?php if (\$_ctx->archives->isStart()) : ?>".
490          $content.
491          "<?php endif; ?>";
492     }
493     
494     /*dtd
495     <!ELEMENT tpl:ArchivesFooter - - -- Last archives result container -->
496     */
497     public function ArchivesFooter($attr,$content)
498     {
499          return
500          "<?php if (\$_ctx->archives->isEnd()) : ?>".
501          $content.
502          "<?php endif; ?>";
503     }
504     
505     /*dtd
506     <!ELEMENT tpl:ArchivesYearHeader - - -- First result of year in archives container -->
507     */
508     public function ArchivesYearHeader($attr,$content)
509     {
510          return
511          "<?php if (\$_ctx->archives->yearHeader()) : ?>".
512          $content.
513          "<?php endif; ?>";
514     }
515     
516     /*dtd
517     <!ELEMENT tpl:ArchivesYearFooter - - -- Last result of year in archives container -->
518     */
519     public function ArchivesYearFooter($attr,$content)
520     {
521          return
522          "<?php if (\$_ctx->archives->yearFooter()) : ?>".
523          $content.
524          "<?php endif; ?>";
525     }
526     
527     /*dtd
528     <!ELEMENT tpl:ArchiveDate - O -- Archive result date -->
529     <!ATTLIST tpl:ArchiveDate
530     format    CDATA     #IMPLIED  -- Date format (Default %B %Y) --
531     >
532     */
533     public function ArchiveDate($attr)
534     {
535          $format = '%B %Y';
536          if (!empty($attr['format'])) {
537               $format = addslashes($attr['format']);
538          }
539         
540          $f = $this->getFilters($attr);
541          return '<?php echo '.sprintf($f,"dt::dt2str('".$format."',\$_ctx->archives->dt)").'; ?>';
542     }
543     
544     /*dtd
545     <!ELEMENT tpl:ArchiveEntriesCount - O -- Current archive result number of entries -->
546     */
547     public function ArchiveEntriesCount($attr)
548     {
549          $f = $this->getFilters($attr);
550          return '<?php echo '.sprintf($f,'$_ctx->archives->nb_post').'; ?>';
551     }
552     
553     /*dtd
554     <!ELEMENT tpl:ArchiveNext - - -- Next archive result container -->
555     <!ATTLIST tpl:ArchiveNext
556     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
557     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
558     post_lang CDATA          #IMPLIED  -- Filter on the given language
559     >
560     */
561     public function ArchiveNext($attr,$content)
562     {
563          $p = "if (!isset(\$params)) \$params = array();\n";
564          $p .= "\$params['type'] = 'month';\n";
565          if (isset($attr['type'])) {
566               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
567          }
568         
569          if (isset($attr['post_type'])) {
570               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
571          }
572         
573          if (isset($attr['post_lang'])) {
574               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
575          }
576       
577          $p .= "\$params['next'] = \$_ctx->archives->dt;";
578         
579          $res = "<?php\n";
580          $res .= $p;
581          $res .= $this->core->callBehavior("templatePrepareParams",
582               array("tag" => "ArchiveNext","method" => "blog::getDates"), 
583               $attr, $content);
584          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
585          $res .= "?>\n";
586         
587          $res .=
588          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
589         
590          return $res;
591     }
592     
593     /*dtd
594     <!ELEMENT tpl:ArchivePrevious - - -- Previous archive result container -->
595     <!ATTLIST tpl:ArchivePrevious
596     type      (day|month|year)    #IMPLIED  -- Get days, months or years, default to month --
597     post_type CDATA               #IMPLIED  -- Get dates of given type of entries, default to post --
598     post_lang CDATA          #IMPLIED  -- Filter on the given language
599     >
600     */
601     public function ArchivePrevious($attr,$content)
602     {
603          $p = 'if (!isset($params)) $params = array();';
604          $p .= "\$params['type'] = 'month';\n";
605          if (isset($attr['type'])) {
606               $p .= "\$params['type'] = '".addslashes($attr['type'])."';\n";
607          }
608         
609          if (isset($attr['post_type'])) {
610               $p .= "\$params['post_type'] = '".addslashes($attr['post_type'])."';\n";
611          }
612       
613          if (isset($attr['post_lang'])) {
614               $p .= "\$params['post_lang'] = '".addslashes($attr['post_lang'])."';\n";
615          }
616       
617          $p .= "\$params['previous'] = \$_ctx->archives->dt;";
618         
619          $res = "<?php\n";
620          $res .= $this->core->callBehavior("templatePrepareParams",
621               array("tag" => "ArchivePrevious","method" => "blog::getDates"), 
622               $attr, $content);
623          $res .= $p;
624          $res .= '$_ctx->archives = $core->blog->getDates($params); unset($params);'."\n";
625          $res .= "?>\n";
626         
627          $res .=
628          '<?php while ($_ctx->archives->fetch()) : ?>'.$content.'<?php endwhile; $_ctx->archives = null; ?>';
629         
630          return $res;
631     }
632     
633     /*dtd
634     <!ELEMENT tpl:ArchiveURL - O -- Current archive result URL -->
635     */
636     public function ArchiveURL($attr)
637     {
638          $f = $this->getFilters($attr);
639          return '<?php echo '.sprintf($f,'$_ctx->archives->url($core)').'; ?>';
640     }
641     
642     
643     /* Blog ----------------------------------------------- */
644     /*dtd
645     <!ELEMENT tpl:BlogArchiveURL - O -- Blog Archives URL -->
646     */
647     public function BlogArchiveURL($attr)
648     {
649          $f = $this->getFilters($attr);
650          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("archive")').'; ?>';
651     }
652     
653     /*dtd
654     <!ELEMENT tpl:BlogCopyrightNotice - O -- Blog copyrght notices -->
655     */
656     public function BlogCopyrightNotice($attr)
657     {
658          $f = $this->getFilters($attr);
659          return '<?php echo '.sprintf($f,'$core->blog->settings->system->copyright_notice').'; ?>';
660     }
661     
662     /*dtd
663     <!ELEMENT tpl:BlogDescription - O -- Blog Description -->
664     */
665     public function BlogDescription($attr)
666     {
667          $f = $this->getFilters($attr);
668          return '<?php echo '.sprintf($f,'$core->blog->desc').'; ?>';
669     }
670     
671     /*dtd
672     <!ELEMENT tpl:BlogEditor - O -- Blog Editor -->
673     */
674     public function BlogEditor($attr)
675     {
676          $f = $this->getFilters($attr);
677          return '<?php echo '.sprintf($f,'$core->blog->settings->system->editor').'; ?>';
678     }
679     
680     /*dtd
681     <!ELEMENT tpl:BlogFeedID - O -- Blog Feed ID -->
682     */
683     public function BlogFeedID($attr)
684     {
685          $f = $this->getFilters($attr);
686          return '<?php echo '.sprintf($f,'"urn:md5:".$core->blog->uid').'; ?>';
687     }
688     
689     /*dtd
690     <!ELEMENT tpl:BlogFeedURL - O -- Blog Feed URL -->
691     <!ATTLIST tpl:BlogFeedURL
692     type (rss2|atom)    #IMPLIED  -- feed type (default : rss2)
693     >
694     */
695     public function BlogFeedURL($attr)
696     {
697          $type = !empty($attr['type']) ? $attr['type'] : 'atom';
698         
699          if (!preg_match('#^(rss2|atom)$#',$type)) {
700               $type = 'atom';
701          }
702         
703          $f = $this->getFilters($attr);
704          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("feed","'.$type.'")').'; ?>';
705     }
706     
707     /*dtd
708     <!ELEMENT tpl:BlogName - O -- Blog Name -->
709     */
710     public function BlogName($attr)
711     {
712          $f = $this->getFilters($attr);
713          return '<?php echo '.sprintf($f,'$core->blog->name').'; ?>';
714     }
715     
716     /*dtd
717     <!ELEMENT tpl:BlogLanguage - O -- Blog Language -->
718     */
719     public function BlogLanguage($attr)
720     {
721          $f = $this->getFilters($attr);
722          return '<?php echo '.sprintf($f,'$core->blog->settings->system->lang').'; ?>';
723     }
724     
725     /*dtd
726     <!ELEMENT tpl:BlogThemeURL - O -- Blog's current Themei URL -->
727     */
728     public function BlogThemeURL($attr)
729     {
730          $f = $this->getFilters($attr);
731          return '<?php echo '.sprintf($f,'$core->blog->settings->system->themes_url."/".$core->blog->settings->system->theme').'; ?>';
732     }
733   
734     /*dtd
735     <!ELEMENT tpl:BlogPublicURL - O -- Blog Public directory URL -->
736     */
737     public function BlogPublicURL($attr)
738     {
739          $f = $this->getFilters($attr);
740          return '<?php echo '.sprintf($f,'$core->blog->settings->system->public_url').'; ?>';
741     }
742     
743     /*dtd
744     <!ELEMENT tpl:BlogUpdateDate - O -- Blog last update date -->
745     <!ATTLIST tpl:BlogUpdateDate
746     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
747     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
748     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
749     >
750     */
751     public function BlogUpdateDate($attr)
752     {
753          $format = '';
754          if (!empty($attr['format'])) {
755               $format = addslashes($attr['format']);
756          } else {
757               $format = '%Y-%m-%d %H:%M:%S';
758          }
759         
760          $iso8601 = !empty($attr['iso8601']);
761          $rfc822 = !empty($attr['rfc822']);
762         
763          $f = $this->getFilters($attr);
764         
765          if ($rfc822) {
766               return '<?php echo '.sprintf($f,"dt::rfc822(\$core->blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>';
767          } elseif ($iso8601) {
768               return '<?php echo '.sprintf($f,"dt::iso8601(\$core->blog->upddt,\$core->blog->settings->system->blog_timezone)").'; ?>';
769          } else {
770               return '<?php echo '.sprintf($f,"dt::str('".$format."',\$core->blog->upddt)").'; ?>';
771          }
772     }
773     
774     /*dtd
775     <!ELEMENT tpl:BlogID - 0 -- Blog ID -->
776     */
777     public function BlogID($attr)
778     {
779          $f = $this->getFilters($attr);
780          return '<?php echo '.sprintf($f,'$core->blog->id').'; ?>';
781     }
782     
783     /*dtd
784     <!ELEMENT tpl:BlogRSDURL - O -- Blog RSD URL -->
785     */
786     public function BlogRSDURL($attr)
787     {
788          $f = $this->getFilters($attr);
789          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor(\'rsd\')').'; ?>';
790     }
791     
792     /*dtd
793     <!ELEMENT tpl:BlogURL - O -- Blog URL -->
794     */
795     public function BlogURL($attr)
796     {
797          $f = $this->getFilters($attr);
798          return '<?php echo '.sprintf($f,'$core->blog->url').'; ?>';
799     }
800     
801     /*dtd
802     <!ELEMENT tpl:BlogQmarkURL - O -- Blog URL, ending with a question mark -->
803     */
804     public function BlogQmarkURL($attr)
805     {
806          $f = $this->getFilters($attr);
807          return '<?php echo '.sprintf($f,'$core->blog->getQmarkURL()').'; ?>';
808     }
809     
810     /*dtd
811     <!ELEMENT tpl:BlogMetaRobots - O -- Blog meta robots tag definition, overrides robots_policy setting -->
812     <!ATTLIST tpl:BlogMetaRobots
813     robots    CDATA     #IMPLIED  -- can be INDEX,FOLLOW,NOINDEX,NOFOLLOW,ARCHIVE,NOARCHIVE
814     >
815     */
816     public function BlogMetaRobots($attr)
817     {
818          $robots = isset($attr['robots']) ? addslashes($attr['robots']) : '';
819          return "<?php echo context::robotsPolicy(\$core->blog->settings->system->robots_policy,'".$robots."'); ?>";
820     }
821     
822     /* Entries -------------------------------------------- */
823     /*dtd
824     <!ELEMENT tpl:Entries - - -- Blog Entries loop -->
825     <!ATTLIST tpl:Entries
826     lastn     CDATA     #IMPLIED  -- limit number of results to specified value
827     author    CDATA     #IMPLIED  -- get entries for a given user id
828     no_context (1|0)    #IMPLIED  -- Override context information
829     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)
830     order     (desc|asc)     #IMPLIED  -- specify entries order (default : desc)
831     no_content     (0|1)     #IMPLIED  -- do not retrieve entries content
832     selected  (0|1)     #IMPLIED  -- retrieve posts marked as selected only (value: 1) or not selected only (value: 0)
833     url       CDATA     #IMPLIED  -- retrieve post by its url
834     type      CDATA     #IMPLIED  -- retrieve post with given post_type (there can be many ones separated by comma)
835     age       CDATA     #IMPLIED  -- retrieve posts by maximum age (ex: -2 days, last month, last week)
836     ignore_pagination   (0|1)     #IMPLIED  -- ignore page number provided in URL (useful when using multiple tpl:Entries on the same page)
837     >
838     */
839     public function Entries($attr,$content)
840     {
841          $lastn = -1;
842          if (isset($attr['lastn'])) {
843               $lastn = abs((integer) $attr['lastn'])+0;
844          }
845         
846          $p = 'if (!isset($_page_number)) { $_page_number = 1; }'."\n";
847         
848          if ($lastn != 0) {
849               if ($lastn > 0) {
850                    $p .= "\$params['limit'] = ".$lastn.";\n";
851               } else {
852                    $p .= "\$params['limit'] = \$_ctx->nb_entry_per_page;\n";
853               }
854               
855               if (!isset($attr['ignore_pagination']) || $attr['ignore_pagination'] == "0") {
856                    $p .= "\$params['limit'] = array(((\$_page_number-1)*\$params['limit']),\$params['limit']);\n";
857               } else {
858                    $p .= "\$params['limit'] = array(0, \$params['limit']);\n";
859               }
860          }
861         
862          if (isset($attr['author'])) {
863               $p .= "\$params['user_id'] = '".addslashes($attr['author'])."';\n";
864          }
865         
866          if (!empty($attr['type'])) {
867               $p .= "\$params['post_type'] = preg_split('/\s*,\s*/','".addslashes($attr['type'])."',-1,PREG_SPLIT_NO_EMPTY);\n";
868          }
869         
870          if (!empty($attr['url'])) {
871               $p .= "\$params['post_url'] = '".addslashes($attr['url'])."';\n";
872          }
873         
874          if (empty($attr['no_context']))
875          {
876               if (!isset($attr['author']))
877               {
878                    $p .=
879                    'if ($_ctx->exists("users")) { '.
880                         "\$params['user_id'] = \$_ctx->users->user_id; ".
881                    "}\n";
882               }
883               
884               $p .=
885               'if ($_ctx->exists("archives")) { '.
886                    "\$params['post_year'] = \$_ctx->archives->year(); ".
887                    "\$params['post_month'] = \$_ctx->archives->month(); ";
888               if (!isset($attr['lastn'])) {
889                    $p .= "unset(\$params['limit']); ";
890               }
891               $p .=
892               "}\n";
893               
894               $p .=
895               'if ($_ctx->exists("langs")) { '.
896                    "\$params['post_lang'] = \$_ctx->langs->post_lang; ".
897               "}\n";
898               
899               $p .=
900               'if (isset($_search)) { '.
901                    "\$params['search'] = \$_search; ".
902               "}\n";
903          }
904         
905          $p .= "\$params['order'] = '".$this->getSortByStr($attr,'post')."';\n";
906         
907          if (isset($attr['no_content']) && $attr['no_content']) {
908               $p .= "\$params['no_content'] = true;\n";
909          }
910         
911          if (isset($attr['selected'])) {
912               $p .= "\$params['post_selected'] = ".(integer) (boolean) $attr['selected'].";";
913          }
914         
915          if (isset($attr['age'])) {
916               $age = $this->getAge($attr);
917               $p .= !empty($age) ? "@\$params['sql'] .= ' AND P.post_dt > \'".$age."\'';\n" : '';
918          }
919         
920          $res = "<?php\n";
921          $res .= $p;
922          $res .= $this->core->callBehavior("templatePrepareParams", 
923               array("tag" => "Entries","method" => "blog::getPosts"), 
924               $attr,$content);
925          $res .= '$_ctx->post_params = $params;'."\n";
926          $res .= '$_ctx->posts = $core->blog->getPosts($params); unset($params);'."\n";
927          $res .= "?>\n";
928          $res .=
929          '<?php while ($_ctx->posts->fetch()) : ?>'.$content.'<?php endwhile; '.
930          '$_ctx->posts = null; $_ctx->post_params = null; ?>';
931         
932          return $res;
933     }
934     
935     /*dtd
936     <!ELEMENT tpl:DateHeader - O -- Displays date, if post is the first post of the given day -->
937     */
938     public function DateHeader($attr,$content)
939     {
940          return
941          "<?php if (\$_ctx->posts->firstPostOfDay()) : ?>".
942          $content.
943          "<?php endif; ?>";
944     }
945     
946     /*dtd
947     <!ELEMENT tpl:DateFooter - O -- Displays date,  if post is the last post of the given day -->
948     */
949     public function DateFooter($attr,$content)
950     {
951          return
952          "<?php if (\$_ctx->posts->lastPostOfDay()) : ?>".
953          $content.
954          "<?php endif; ?>";
955     }
956     
957     /*dtd
958     <!ELEMENT tpl:EntryIf - - -- tests on current entry -->
959     <!ATTLIST tpl:EntryIf
960     type CDATA     #IMPLIED  -- post has a given type (default: "post")
961     first     (0|1)     #IMPLIED  -- post is the first post from list (value : 1) or not (value : 0)
962     odd  (0|1)     #IMPLIED  -- post is in an odd position (value : 1) or not (value : 0)
963     even (0|1)     #IMPLIED  -- post is in an even position (value : 1) or not (value : 0)
964     extended  (0|1)     #IMPLIED  -- post has an excerpt (value : 1) or not (value : 0)
965     selected  (0|1)     #IMPLIED  -- post is selected (value : 1) or not (value : 0)
966     has_attachment (0|1)     #IMPLIED  -- post has attachments (value : 1) or not (value : 0) (see Attachment plugin for code)
967     operator  (and|or)  #IMPLIED  -- combination of conditions, if more than 1 specifiec (default: and)
968     url       CDATA     #IMPLIED  -- post has given url
969     >
970     */
971     public function EntryIf($attr,$content)
972     {
973          $if = new ArrayObject();
974          $extended = null;
975         
976          $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
977       
978          if (isset($attr['type'])) {
979               $type = trim($attr['type']);
980               $type = !empty($type)?$type:'post';
981               $if[] = '$_ctx->posts->post_type == "'.addslashes($type).'"';
982          }
983         
984          if (isset($attr['url'])) {
985               $url = trim($attr['url']);
986               if (substr($url,0,1) == '!') {
987                    $url = substr($url,1);
988                    $if[] = '$_ctx->posts->post_url != "'.addslashes($url).'"';
989               } else {
990                    $if[] = '$_ctx->posts->post_url == "'.addslashes($url).'"';
991               }
992          }
993         
994          if (isset($attr['first'])) {
995               $sign = (boolean) $attr['first'] ? '=' : '!';
996               $if[] = '$_ctx->posts->index() '.$sign.'= 0';
997          }
998         
999          if (isset($attr['odd'])) {
1000               $sign = (boolean) $attr['odd'] ? '=' : '!';
1001               $if[] = '($_ctx->posts->index()+1)%2 '.$sign.'= 1';
1002          }
1003         
1004          if (isset($attr['extended'])) {
1005               $sign = (boolean) $attr['extended'] ? '' : '!';
1006               $if[] = $sign.'$_ctx->posts->isExtended()';
1007          }
1008         
1009          if (isset($attr['selected'])) {
1010               $sign = (boolean) $attr['selected'] ? '' : '!';
1011               $if[] = $sign.'(boolean)$_ctx->posts->post_selected';
1012          }
1013         
1014          $this->core->callBehavior('tplIfConditions','EntryIf',$attr,$content,$if);
1015         
1016          if (count($if) != 0) {
1017               return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
1018          } /*
1019          else {
1020               // Nothing in if statement => do not display content...
1021               return $content;
1022          }
1023          */
1024     }
1025     
1026     /*dtd
1027     <!ELEMENT tpl:EntryIfFirst - O -- displays value if entry is the first one -->
1028     <!ATTLIST tpl:EntryIfFirst
1029     return    CDATA     #IMPLIED  -- value to display in case of success (default: first)
1030     >
1031     */
1032     public function EntryIfFirst($attr)
1033     {
1034          $ret = isset($attr['return']) ? $attr['return'] : 'first';
1035          $ret = html::escapeHTML($ret);
1036         
1037          return
1038          '<?php if ($_ctx->posts->index() == 0) { '.
1039          "echo '".addslashes($ret)."'; } ?>";
1040     }
1041     
1042     /*dtd
1043     <!ELEMENT tpl:EntryIfOdd - O -- displays value if entry is in an odd position -->
1044     <!ATTLIST tpl:EntryIfOdd
1045     return    CDATA     #IMPLIED  -- value to display in case of success (default: odd)
1046     >
1047     */
1048     public function EntryIfOdd($attr)
1049     {
1050          $ret = isset($attr['return']) ? $attr['return'] : 'odd';
1051          $ret = html::escapeHTML($ret);
1052         
1053          return
1054          '<?php if (($_ctx->posts->index()+1)%2 == 1) { '.
1055          "echo '".addslashes($ret)."'; } ?>";
1056     }
1057     
1058     /*dtd
1059     <!ELEMENT tpl:EntryIfSelected - O -- displays value if entry is selected -->
1060     <!ATTLIST tpl:EntryIfSelected
1061     return    CDATA     #IMPLIED  -- value to display in case of success (default: selected)
1062     >
1063     */
1064     public function EntryIfSelected($attr)
1065     {
1066          $ret = isset($attr['return']) ? $attr['return'] : 'selected';
1067          $ret = html::escapeHTML($ret);
1068         
1069          return
1070          '<?php if ($_ctx->posts->post_selected) { '.
1071          "echo '".addslashes($ret)."'; } ?>";
1072     }
1073     
1074     /*dtd
1075     <!ELEMENT tpl:EntryContent - O -- Entry content -->
1076     <!ATTLIST tpl:EntryContent
1077     absolute_urls  CDATA     #IMPLIED -- transforms local URLs to absolute one
1078     full           (1|0)     #IMPLIED -- returns full content with excerpt
1079     >
1080     */
1081     public function EntryContent($attr)
1082     {
1083          $urls = '0';
1084          if (!empty($attr['absolute_urls'])) {
1085               $urls = '1';
1086          }
1087         
1088          $f = $this->getFilters($attr);
1089         
1090          if (!empty($attr['full'])) {
1091               return '<?php echo '.sprintf($f,
1092                    '$_ctx->posts->getExcerpt('.$urls.')." ".$_ctx->posts->getContent('.$urls.')').'; ?>';
1093          } else {
1094               return '<?php echo '.sprintf($f,'$_ctx->posts->getContent('.$urls.')').'; ?>';
1095          }
1096     }
1097     
1098     /*dtd
1099     <!ELEMENT tpl:EntryExcerpt - O -- Entry excerpt -->
1100     <!ATTLIST tpl:EntryExcerpt
1101     absolute_urls  CDATA     #IMPLIED -- transforms local URLs to absolute one
1102     >
1103     */
1104     public function EntryExcerpt($attr)
1105     {
1106          $urls = '0';
1107          if (!empty($attr['absolute_urls'])) {
1108               $urls = '1';
1109          }
1110         
1111          $f = $this->getFilters($attr);
1112          return '<?php echo '.sprintf($f,'$_ctx->posts->getExcerpt('.$urls.')').'; ?>';
1113     }
1114     
1115     
1116     /*dtd
1117     <!ELEMENT tpl:EntryAuthorCommonName - O -- Entry author common name -->
1118     */
1119     public function EntryAuthorCommonName($attr)
1120     {
1121          $f = $this->getFilters($attr);
1122          return '<?php echo '.sprintf($f,'$_ctx->posts->getAuthorCN()').'; ?>';
1123     }
1124     
1125     /*dtd
1126     <!ELEMENT tpl:EntryAuthorDisplayName - O -- Entry author display name -->
1127     */
1128     public function EntryAuthorDisplayName($attr)
1129     {
1130          $f = $this->getFilters($attr);
1131          return '<?php echo '.sprintf($f,'$_ctx->posts->user_displayname').'; ?>';
1132     }
1133     
1134     /*dtd
1135     <!ELEMENT tpl:EntryAuthorID - O -- Entry author ID -->
1136     */
1137     public function EntryAuthorID($attr)
1138     {
1139          $f = $this->getFilters($attr);
1140          return '<?php echo '.sprintf($f,'$_ctx->posts->user_id').'; ?>';
1141     }
1142     
1143     /*dtd
1144     <!ELEMENT tpl:EntryAuthorEmail - O -- Entry author email -->
1145     <!ATTLIST tpl:EntryAuthorEmail
1146     spam_protected (0|1)     #IMPLIED  -- protect email from spam (default: 1)
1147     >
1148     */
1149     public function EntryAuthorEmail($attr)
1150     {
1151          $p = 'true';
1152          if (isset($attr['spam_protected']) && !$attr['spam_protected']) {
1153               $p = 'false';
1154          }
1155         
1156          $f = $this->getFilters($attr);
1157          return '<?php echo '.sprintf($f,"\$_ctx->posts->getAuthorEmail(".$p.")").'; ?>';
1158     }
1159     
1160     /*dtd
1161     <!ELEMENT tpl:EntryAuthorLink - O -- Entry author link -->
1162     */
1163     public function EntryAuthorLink($attr)
1164     {
1165          $f = $this->getFilters($attr);
1166          return '<?php echo '.sprintf($f,'$_ctx->posts->getAuthorLink()').'; ?>';
1167     }
1168     
1169     /*dtd
1170     <!ELEMENT tpl:EntryAuthorURL - O -- Entry author URL -->
1171     */
1172     public function EntryAuthorURL($attr)
1173     {
1174          $f = $this->getFilters($attr);
1175          return '<?php echo '.sprintf($f,'$_ctx->posts->user_url').'; ?>';
1176     }
1177     
1178     /*dtd
1179     <!ELEMENT tpl:EntryBasename - O -- Entry short URL (relative to /post) -->
1180     */
1181     public function EntryBasename($attr)
1182     {
1183          $f = $this->getFilters($attr);
1184          return '<?php echo '.sprintf($f,'$_ctx->posts->post_url').'; ?>';
1185     }
1186     
1187     /*dtd
1188     <!ELEMENT tpl:EntryFeedID - O -- Entry feed ID -->
1189     */
1190     public function EntryFeedID($attr)
1191     {
1192          $f = $this->getFilters($attr);
1193          return '<?php echo '.sprintf($f,'$_ctx->posts->getFeedID()').'; ?>';
1194     }
1195     
1196     /*dtd
1197     <!ELEMENT tpl:EntryFirstImage - O -- Extracts entry first image if exists -->
1198     <!ATTLIST tpl:EntryAuthorEmail
1199     size           (sq|t|s|m|o)   #IMPLIED  -- Image size to extract
1200     class          CDATA          #IMPLIED  -- Class to add on image tag
1201     >
1202     */
1203     public function EntryFirstImage($attr)
1204     {
1205          $size = !empty($attr['size']) ? $attr['size'] : '';
1206          $class = !empty($attr['class']) ? $attr['class'] : '';
1207          $with_category = !empty($attr['with_category']) ? 'true' : 'false';
1208         
1209          return "<?php echo context::EntryFirstImageHelper('".addslashes($size)."','".addslashes($class)."'); ?>";
1210     }
1211     
1212     /*dtd
1213     <!ELEMENT tpl:EntryID - O -- Entry ID -->
1214     */
1215     public function EntryID($attr)
1216     {
1217          $f = $this->getFilters($attr);
1218          return '<?php echo '.sprintf($f,'$_ctx->posts->post_id').'; ?>';
1219     }
1220     
1221     /*dtd
1222     <!ELEMENT tpl:EntryLang - O --  Entry language or blog lang if not defined -->
1223     */
1224     public function EntryLang($attr)
1225     {
1226          $f = $this->getFilters($attr);
1227          return
1228          '<?php if ($_ctx->posts->post_lang) { '.
1229               'echo '.sprintf($f,'$_ctx->posts->post_lang').'; '.
1230          '} else {'.
1231               'echo '.sprintf($f,'$core->blog->settings->system->lang').'; '.
1232          '} ?>';
1233     }
1234     
1235     /*dtd
1236     <!ELEMENT tpl:EntryNext - - -- Next entry block -->
1237     <!ATTLIST tpl:EntryNext
1238     restrict_to_lang         (0|1)     #IMPLIED  -- find next post in the same language (default: 0)
1239     >
1240     */
1241     public function EntryNext($attr,$content)
1242     {
1243          $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0';
1244          $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0';
1245         
1246          return
1247          '<?php $next_post = $core->blog->getNextPost($_ctx->posts,1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n".
1248          '<?php if ($next_post !== null) : ?>'.
1249               
1250               '<?php $_ctx->posts = $next_post; unset($next_post);'."\n".
1251               'while ($_ctx->posts->fetch()) : ?>'.
1252               $content.
1253               '<?php endwhile; $_ctx->posts = null; ?>'.
1254          "<?php endif; ?>\n";
1255     }
1256     
1257     /*dtd
1258     <!ELEMENT tpl:EntryPrevious - - -- Previous entry block -->
1259     <!ATTLIST tpl:EntryPrevious
1260     restrict_to_category     (0|1)     #IMPLIED  -- find previous post in the same category (default: 0)
1261     restrict_to_lang         (0|1)     #IMPLIED  -- find next post in the same language (default: 0)
1262     >
1263     */
1264     public function EntryPrevious($attr,$content)
1265     {
1266          $restrict_to_category = !empty($attr['restrict_to_category']) ? '1' : '0';
1267          $restrict_to_lang = !empty($attr['restrict_to_lang']) ? '1' : '0';
1268         
1269          return
1270          '<?php $prev_post = $core->blog->getNextPost($_ctx->posts,-1,'.$restrict_to_category.','.$restrict_to_lang.'); ?>'."\n".
1271          '<?php if ($prev_post !== null) : ?>'.
1272               
1273               '<?php $_ctx->posts = $prev_post; unset($prev_post);'."\n".
1274               'while ($_ctx->posts->fetch()) : ?>'.
1275               $content.
1276               '<?php endwhile; $_ctx->posts = null; ?>'.
1277          "<?php endif; ?>\n";
1278     }
1279     
1280     /*dtd
1281     <!ELEMENT tpl:EntryTitle - O -- Entry title -->
1282     */
1283     public function EntryTitle($attr)
1284     {
1285          $f = $this->getFilters($attr);
1286          return '<?php echo '.sprintf($f,'$_ctx->posts->post_title').'; ?>';
1287     }
1288     
1289     /*dtd
1290     <!ELEMENT tpl:EntryURL - O -- Entry URL -->
1291     */
1292     public function EntryURL($attr)
1293     {
1294          $f = $this->getFilters($attr);
1295          return '<?php echo '.sprintf($f,'$_ctx->posts->getURL()').'; ?>';
1296     }
1297     
1298     /*dtd
1299     <!ELEMENT tpl:EntryDate - O -- Entry date -->
1300     <!ATTLIST tpl:EntryDate
1301     format    CDATA     #IMPLIED  -- date format (encoded in dc:str by default if iso8601 or rfc822 not specified)
1302     iso8601   CDATA     #IMPLIED  -- if set, tells that date format is ISO 8601
1303     rfc822    CDATA     #IMPLIED  -- if set, tells that date format is RFC 822
1304     upddt     CDATA     #IMPLIED  -- if set, uses the post update time
1305     creadt    CDATA     #IMPLIED  -- if set, uses the post creation time
1306     >
1307     */
1308     public function EntryDate($attr)
1309     {
1310          $format = '';
1311          if (!empty($attr['format'])) {
1312               $format = addslashes($attr['format']);
1313          }
1314         
1315          $iso8601 = !empty($attr['iso8601']);
1316          $rfc822 = !empty($attr['rfc822']);
1317          $type = (!empty($attr['creadt']) ? 'creadt' : '');
1318          $type = (!empty($attr['upddt']) ? 'upddt' : $type);
1319         
1320          $f = $this->getFilters($attr);
1321         
1322          if ($rfc822) {
1323               return '<?php echo '.sprintf($f,"\$_ctx->posts->getRFC822Date('".$type."')").'; ?>';
1324          } elseif ($iso8601) {
1325               return '<?php echo '.sprintf($f,"\$_ctx->posts->getISO8601Date('".$type."')").'; ?>';
1326          } else {
1327               return '<?php echo '.sprintf($f,"\$_ctx->posts->getDate('".$format."','".$type."')").'; ?>';
1328          }
1329     }
1330     
1331     /*dtd
1332     <!ELEMENT tpl:EntryTime - O -- Entry date -->
1333     <!ATTLIST tpl:EntryTime
1334     format    CDATA     #IMPLIED  -- time format
1335     upddt     CDATA     #IMPLIED  -- if set, uses the post update time
1336     creadt    CDATA     #IMPLIED  -- if set, uses the post creation time
1337     >
1338     */
1339     public function EntryTime($attr)
1340     {
1341          $format = '';
1342          if (!empty($attr['format'])) {
1343               $format = addslashes($attr['format']);
1344          }
1345         
1346          $type = (!empty($attr['creadt']) ? 'creadt' : '');
1347          $type = (!empty($attr['upddt']) ? 'upddt' : $type);
1348       
1349          $f = $this->getFilters($attr);
1350          return '<?php echo '.sprintf($f,"\$_ctx->posts->getTime('".$format."','".$type."')").'; ?>';
1351     }
1352     
1353     /*dtd
1354     <!ELEMENT tpl:EntriesHeader - - -- First entries result container -->
1355     */
1356     public function EntriesHeader($attr,$content)
1357     {
1358          return
1359          "<?php if (\$_ctx->posts->isStart()) : ?>".
1360          $content.
1361          "<?php endif; ?>";
1362     }
1363     
1364     /*dtd
1365     <!ELEMENT tpl:EntriesFooter - - -- Last entries result container -->
1366     */
1367     public function EntriesFooter($attr,$content)
1368     {
1369          return
1370          "<?php if (\$_ctx->posts->isEnd()) : ?>".
1371          $content.
1372          "<?php endif; ?>";
1373     }
1374     
1375     /* Languages -------------------------------------- */
1376     /*dtd
1377     <!ELEMENT tpl:Languages - - -- Languages loop -->
1378     <!ATTLIST tpl:Languages
1379     lang CDATA     #IMPLIED  -- restrict loop on given lang
1380     order     (desc|asc)     #IMPLIED  -- languages ordering (default: desc)
1381     >
1382     */
1383     public function Languages($attr,$content)
1384     {
1385          $p = "if (!isset(\$params)) \$params = array();\n";
1386         
1387          if (isset($attr['lang'])) {
1388               $p = "\$params['lang'] = '".addslashes($attr['lang'])."';\n";
1389          }
1390         
1391          $order = 'desc';
1392          if (isset($attr['order']) && preg_match('/^(desc|asc)$/i',$attr['order'])) {
1393               $p .= "\$params['order'] = '".$attr['order']."';\n ";
1394          }
1395         
1396          $res = "<?php\n";
1397          $res .= $p;
1398          $res .= $this->core->callBehavior("templatePrepareParams", 
1399               array("tag" => "Languages","method" => "blog::getLangs"), 
1400               $attr,$content);
1401          $res .= '$_ctx->langs = $core->blog->getLangs($params); unset($params);'."\n";
1402          $res .= "?>\n";
1403         
1404          $res .=
1405          '<?php if ($_ctx->langs->count() > 1) : '.
1406          'while ($_ctx->langs->fetch()) : ?>'.$content.
1407          '<?php endwhile; $_ctx->langs = null; endif; ?>';
1408         
1409          return $res;
1410     }
1411     
1412     /*dtd
1413     <!ELEMENT tpl:LanguagesHeader - - -- First languages result container -->
1414     */
1415     public function LanguagesHeader($attr,$content)
1416     {
1417          return
1418          "<?php if (\$_ctx->langs->isStart()) : ?>".
1419          $content.
1420          "<?php endif; ?>";
1421     }
1422     
1423     /*dtd
1424     <!ELEMENT tpl:LanguagesFooter - - -- Last languages result container -->
1425     */
1426     public function LanguagesFooter($attr,$content)
1427     {
1428          return
1429          "<?php if (\$_ctx->langs->isEnd()) : ?>".
1430          $content.
1431          "<?php endif; ?>";
1432     }
1433     
1434     /*dtd
1435     <!ELEMENT tpl:LanguageCode - O -- Language code -->
1436     */
1437     public function LanguageCode($attr)
1438     {
1439          $f = $this->getFilters($attr);
1440          return '<?php echo '.sprintf($f,'$_ctx->langs->post_lang').'; ?>';
1441     }
1442     
1443     /*dtd
1444     <!ELEMENT tpl:LanguageIfCurrent - - -- tests if post language is current language -->
1445     */
1446     public function LanguageIfCurrent($attr,$content)
1447     {
1448          return
1449          "<?php if (\$_ctx->cur_lang == \$_ctx->langs->post_lang) : ?>".
1450          $content.
1451          "<?php endif; ?>";
1452     }
1453     
1454     /*dtd
1455     <!ELEMENT tpl:LanguageURL - O -- Language URL -->
1456     */
1457     public function LanguageURL($attr)
1458     {
1459          $f = $this->getFilters($attr);
1460          return '<?php echo '.sprintf($f,'$core->blog->url.$core->url->getURLFor("lang",'.
1461               '$_ctx->langs->post_lang)').'; ?>';
1462     }
1463     
1464     /* Pagination ------------------------------------- */
1465     /*dtd
1466     <!ELEMENT tpl:Pagination - - -- Pagination container -->
1467     <!ATTLIST tpl:Pagination
1468     no_context     (0|1)     #IMPLIED  -- override test on posts count vs number of posts per page
1469     >
1470     */
1471     public function Pagination($attr,$content)
1472     {
1473          $p = "<?php\n";
1474          $p .= '$params = $_ctx->post_params;'."\n";
1475          $p .= $this->core->callBehavior("templatePrepareParams", 
1476               array("tag" => "Pagination","method" => "blog::getPosts"), 
1477               $attr,$content);
1478          $p .= '$_ctx->pagination = $core->blog->getPosts($params,true); unset($params);'."\n";
1479          $p .= "?>\n";
1480         
1481          if (isset($attr['no_context']) && $attr['no_context']) {
1482               return $p.$content;
1483          }
1484       
1485          return
1486               $p.
1487               '<?php if ($_ctx->pagination->f(0) > $_ctx->posts->count()) : ?>'.
1488               $content.
1489               '<?php endif; ?>';
1490     }
1491     
1492     /*dtd
1493     <!ELEMENT tpl:PaginationCounter - O -- Number of pages -->
1494     */
1495     public function PaginationCounter($attr)
1496     {
1497          $f = $this->getFilters($attr);
1498          return '<?php echo '.sprintf($f,"context::PaginationNbPages()").'; ?>';
1499     }
1500     
1501     /*dtd
1502     <!ELEMENT tpl:PaginationCurrent - O -- current page -->
1503     */
1504     public function PaginationCurrent($attr)
1505     {
1506          $offset = 0;
1507          if (isset($attr['offset'])) {
1508               $offset = (integer) $attr['offset'];
1509          }
1510         
1511          $f = $this->getFilters($attr);
1512          return '<?php echo '.sprintf($f,"context::PaginationPosition(".$offset.")").'; ?>';
1513     }
1514     
1515     /*dtd
1516     <!ELEMENT tpl:PaginationIf - - -- pages tests -->
1517     <!ATTLIST tpl:PaginationIf
1518     start     (0|1)     #IMPLIED  -- test if we are at first page (value : 1) or not (value : 0)
1519     end  (0|1)     #IMPLIED  -- test if we are at last page (value : 1) or not (value : 0)
1520     >
1521     */
1522     public function PaginationIf($attr,$content)
1523     {
1524          $if = array();
1525         
1526          if (isset($attr['start'])) {
1527               $sign = (boolean) $attr['start'] ? '' : '!';
1528               $if[] = $sign.'context::PaginationStart()';
1529          }
1530         
1531          if (isset($attr['end'])) {
1532               $sign = (boolean) $attr['end'] ? '' : '!';
1533               $if[] = $sign.'context::PaginationEnd()';
1534          }
1535         
1536          $this->core->callBehavior('tplIfConditions','PaginationIf',$attr,$content,$if);
1537         
1538          if (count($if) != 0) {
1539               return '<?php if('.implode(' && ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
1540          } else {
1541               return $content;
1542          }
1543     }
1544     
1545     /*dtd
1546     <!ELEMENT tpl:PaginationURL - O -- link to previoux/next page -->
1547     <!ATTLIST tpl:PaginationURL
1548     offset    CDATA     #IMPLIED  -- page offset (negative for previous pages), default: 0
1549     >
1550     */
1551     public function PaginationURL($attr)
1552     {
1553          $offset = 0;
1554          if (isset($attr['offset'])) {
1555               $offset = (integer) $attr['offset'];
1556          }
1557         
1558          $f = $this->getFilters($attr);
1559          return '<?php echo '.sprintf($f,"context::PaginationURL(".$offset.")").'; ?>';
1560     }
1561     
1562     # System
1563     /*dtd
1564     <!ELEMENT tpl:SysBehavior - O -- Call a given behavior -->
1565     <!ATTLIST tpl:SysBehavior
1566     behavior  CDATA     #IMPLIED  -- behavior to call
1567     >
1568     */
1569     public function SysBehavior($attr,$raw)
1570     {
1571          if (!isset($attr['behavior'])) {
1572               return;
1573          }
1574         
1575          $b = addslashes($attr['behavior']);
1576          return
1577          '<?php if ($core->hasBehavior(\''.$b.'\')) { '.
1578               '$core->callBehavior(\''.$b.'\',$core,$_ctx);'.
1579          '} ?>';
1580     }
1581     
1582     /*dtd
1583     <!ELEMENT tpl:SysIf - - -- System settings tester container -->
1584     <!ATTLIST tpl:SysIf
1585     posts               (0|1)     #IMPLIED  -- test if posts are set in current context (value : 1) or not (value : 0)
1586     blog_lang           CDATA     #IMPLIED  -- tests if blog language is the one given in parameter
1587     current_tpl         CDATA     #IMPLIED  -- tests if current template is the one given in paramater
1588     current_mode        CDATA     #IMPLIED  -- tests if current URL mode is the one given in parameter
1589     has_tpl             CDATA     #IMPLIED  -- tests if a named template exists
1590     has_tag             CDATA     #IMPLIED  -- tests if a named template tag exists (see Tag plugin for code)
1591     blog_id             CDATA     #IMPLIED  -- tests if current blog ID is the one given in parameter
1592     operator            (and|or)  #IMPLIED  -- combination of conditions, if more than 1 specifiec (default: and)
1593     >
1594     */
1595     public function SysIf($attr,$content)
1596     {
1597          $if = new ArrayObject();
1598          $is_ping = null;
1599         
1600          $operator = isset($attr['operator']) ? $this->getOperator($attr['operator']) : '&&';
1601         
1602          if (isset($attr['posts'])) {
1603               $sign = (boolean) $attr['posts'] ? '!' : '=';
1604               $if[] = '$_ctx->posts '.$sign.'== null';
1605          }
1606         
1607          if (isset($attr['blog_lang'])) {
1608               $if[] = "\$core->blog->settings->system->lang == '".addslashes($attr['blog_lang'])."'";
1609          }
1610         
1611          if (isset($attr['current_tpl'])) {
1612               $sign = '=';
1613               if (substr($attr['current_tpl'],0,1) == '!') {
1614                    $sign = '!';
1615                    $attr['current_tpl'] = substr($attr['current_tpl'],1);
1616               }
1617               $if[] = "\$_ctx->current_tpl ".$sign."= '".addslashes($attr['current_tpl'])."'";
1618          }
1619         
1620          if (isset($attr['current_mode'])) {
1621               $sign = '=';
1622               if (substr($attr['current_mode'],0,1) == '!') {
1623                    $sign = '!';
1624                    $attr['current_mode'] = substr($attr['current_mode'],1);
1625               }
1626               $if[] = "\$core->url->type ".$sign."= '".addslashes($attr['current_mode'])."'";
1627          }
1628         
1629          if (isset($attr['has_tpl'])) {
1630               $sign = '';
1631               if (substr($attr['has_tpl'],0,1) == '!') {
1632                    $sign = '!';
1633                    $attr['has_tpl'] = substr($attr['has_tpl'],1);
1634               }
1635               $if[] = $sign."\$core->tpl->getFilePath('".addslashes($attr['has_tpl'])."') !== false";
1636          }
1637         
1638          if (isset($attr['blog_id'])) {
1639               $sign = '';
1640               if (substr($attr['blog_id'],0,1) == '!') {
1641                    $sign = '!';
1642                    $attr['blog_id'] = substr($attr['blog_id'],1);
1643               }
1644               $if[] = $sign."(\$core->blog->id == '".addslashes($attr['blog_id'])."')";
1645          }
1646         
1647          if (isset($attr['search_count']) &&
1648               preg_match('/^((=|!|&gt;|&lt;)=|(&gt;|&lt;))\s*[0-9]+$/',trim($attr['search_count']))) {
1649               $if[] = '(isset($_search_count) && $_search_count '.html::decodeEntities($attr['search_count']).')';
1650          }
1651         
1652          $this->core->callBehavior('tplIfConditions','SysIf',$attr,$content,$if);
1653         
1654          if (count($if) != 0) {
1655               return '<?php if('.implode(' '.$operator.' ', (array) $if).') : ?>'.$content.'<?php endif; ?>';
1656          } else {
1657               return $content;
1658          }
1659     }
1660     
1661     /*dtd
1662     <!ELEMENT tpl:SysFeedSubtitle - O -- Feed subtitle -->
1663     */
1664     public function SysFeedSubtitle($attr)
1665     {
1666          $f = $this->getFilters($attr);
1667          return '<?php if ($_ctx->feed_subtitle !== null) { echo '.sprintf($f,'$_ctx->feed_subtitle').';} ?>';
1668     }
1669     
1670     /*dtd
1671     <!ELEMENT tpl:SysIfFormError - O -- Container displayed if an error has been detected after form submission -->
1672     */
1673     public function SysIfFormError($attr,$content)
1674     {
1675          return
1676          '<?php if ($_ctx->form_error !== null) : ?>'.
1677          $content.
1678          '<?php endif; ?>';
1679     }
1680     
1681     /*dtd
1682     <!ELEMENT tpl:SysIfFormError - O -- Form error -->
1683     */
1684     public function SysFormError($attr)
1685     {
1686          return
1687          '<?php if ($_ctx->form_error !== null) { echo $_ctx->form_error; } ?>';
1688     }
1689     
1690     public function SysPoweredBy($attr)
1691     {
1692          return
1693          '<?php printf(__("Powered by %s"),"<a href=\"http://dotclear.org/\">Dotclear</a>"); ?>';
1694     }
1695     
1696     public function SysSearchString($attr)
1697     {
1698          $s = isset($attr['string']) ? $attr['string'] : '%1$s';
1699         
1700          $f = $this->getFilters($attr);
1701          return '<?php if (isset($_search)) { echo sprintf(__(\''.$s.'\'),'.sprintf($f,'$_search').',$_search_count);} ?>';
1702     }
1703     
1704     public function SysSelfURI($attr)
1705     {
1706          $f = $this->getFilters($attr);
1707          return '<?php echo '.sprintf($f,'http::getSelfURI()').'; ?>';
1708     }
1709}
1710
1711# Template nodes, for parsing purposes
1712
1713# Generic list node, this one may only be instanciated
1714# once for root element
1715class tplNode 
1716{
1717     # Basic tree structure : links to parent, children forrest
1718     protected $parentNode;
1719     protected $children;
1720     
1721     public function __construct() {
1722          $this->children = array();
1723          $this->parentNode = null;
1724     }
1725     
1726     // Returns compiled block
1727     public function compile($tpl) {
1728          $res='';
1729          foreach ($this->children as $child) {
1730               $res .= $child->compile($tpl);
1731          }
1732          return $res;
1733     }
1734     
1735     # Add a children to current node
1736     public function addChild ($child) {
1737          $this->children[] = $child;
1738          $child->setParent($this);
1739     }
1740     
1741     # Defines parent for current node
1742     protected function setParent($parent) {
1743          $this->parentNode = $parent;
1744     }
1745     
1746     # Retrieves current node parent.
1747     # If parent is root node, null is returned
1748     public function getParent() {
1749          return $this->parentNode;
1750     }
1751     
1752     # Current node tag
1753     public function getTag() {
1754          return "ROOT";
1755     }
1756}
1757
1758// Text node, for any non-tpl content
1759class tplNodeText extends tplNode 
1760{
1761     // Simple text node, only holds its content
1762     protected $content;
1763     
1764     public function __construct($text) {
1765          parent::__construct();
1766          $this->content=$text;
1767     }
1768     
1769     public function compile($tpl) {
1770          return $this->content;
1771     }
1772     
1773     public function getTag() {
1774          return "TEXT";
1775     }
1776}
1777
1778// Block node, for all <tpl:Tag>...</tpl:Tag>
1779class tplNodeBlock extends tplNode 
1780{
1781     protected $attr;
1782     protected $tag;
1783     protected $closed;
1784   
1785     public function __construct($tag,$attr) {
1786          parent::__construct();
1787          $this->content='';
1788          $this->tag = $tag;
1789          $this->attr = $attr;
1790          $this->closed=false;
1791     }
1792     public function setClosing() {
1793          $this->closed = true;
1794     }
1795     public function isClosed() {
1796          return $this->closed;
1797     }
1798     public function compile($tpl) {
1799          if ($this->closed) {
1800               $content = parent::compile($tpl);
1801               return $tpl->compileBlockNode($this->tag,$this->attr,$content);
1802          } else {
1803               // if tag has not been closed, silently ignore its content...
1804               return '';
1805          }
1806     }
1807     public function getTag() {
1808          return $this->tag;
1809     }
1810}
1811
1812// Value node, for all {{tpl:Tag}}
1813class tplNodeValue extends tplNode 
1814{
1815     protected $attr;
1816     protected $str_attr;
1817     protected $tag;
1818     
1819     public function __construct($tag,$attr,$str_attr) {
1820          parent::__construct();
1821          $this->content='';
1822          $this->tag = $tag;
1823          $this->attr = $attr;
1824          $this->str_attr = $str_attr;
1825     }
1826     
1827     public function compile($tpl) {
1828          return $tpl->compileValueNode($this->tag,$this->attr,$this->str_attr);
1829     }
1830     
1831     public function getTag() {
1832          return $this->tag;
1833     }
1834}
1835
1836?>
Note: See TracBrowser for help on using the repository browser.

Sites map