Dotclear

source: inc/public/lib.tpl.context.php @ 3721:c34ce89e190b

Revision 3721:c34ce89e190b, 17.5 KB checked in by franck <carnet.franck.paul@…>, 8 years ago (diff)

Switch to ArrayObject? for template filters args in order to be extended (or modified) by 3rd party plugins (via behaviors)

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) {return;}
13
14class context
15{
16    public $stack = array();
17
18    public function __set($name, $var)
19    {
20        if ($var === null) {
21            $this->pop($name);
22        } else {
23            $this->stack[$name][] = &$var;
24            if ($var instanceof record) {
25                $this->stack['cur_loop'][] = &$var;
26            }
27        }
28    }
29
30    public function __get($name)
31    {
32        if (!isset($this->stack[$name])) {
33            return;
34        }
35
36        $n = count($this->stack[$name]);
37        if ($n > 0) {
38            return $this->stack[$name][($n - 1)];
39        }
40
41        return;
42    }
43
44    public function exists($name)
45    {
46        return isset($this->stack[$name][0]);
47    }
48
49    public function pop($name)
50    {
51        if (isset($this->stack[$name])) {
52            $v = array_pop($this->stack[$name]);
53            if ($v instanceof record) {
54                array_pop($this->stack['cur_loop']);
55            }
56            unset($v);
57        }
58    }
59
60    # Loop position tests
61    public function loopPosition($start, $length = null, $even = null, $modulo = null)
62    {
63        if (!$this->cur_loop) {
64            return false;
65        }
66
67        $index = $this->cur_loop->index();
68        $size  = $this->cur_loop->count();
69
70        $test = false;
71        if ($start >= 0) {
72            $test = $index >= $start;
73            if ($length !== null) {
74                if ($length >= 0) {
75                    $test = $test && $index < $start + $length;
76                } else {
77                    $test = $test && $index < $size + $length;
78                }
79            }
80        } else {
81            $test = $index >= $size + $start;
82            if ($length !== null) {
83                if ($length >= 0) {
84                    $test = $test && $index < $size + $start + $length;
85                } else {
86                    $test = $test && $index < $size + $length;
87                }
88            }
89        }
90
91        if ($even !== null) {
92            $test = $test && $index % 2 == $even;
93        }
94
95        if ($modulo !== null) {
96            $test = $test && ($index % $modulo == 0);
97        }
98
99        return $test;
100    }
101
102    /**
103    @deprecated since version 2.11 , use tpl_context::global_filters instead
104     */
105    public static function global_filter($str,
106        $encode_xml, $remove_html, $cut_string, $lower_case, $upper_case, $encode_url, $tag = '') {
107        return self::global_filters(
108            $str,
109            array(0       => null,
110                'encode_xml'  => $encode_xml,
111                'remove_html' => $remove_html,
112                'cut_string'  => $cut_string,
113                'lower_case'  => $lower_case,
114                'upper_case'  => ($upper_case == 1 ? 1 : 0),
115                'capitalize'  => ($upper_case == 2 ? 1 : 0),
116                'encode_url'  => $encode_url),
117            $tag);
118    }
119
120    public static function global_filters($str, $args, $tag = '')
121    {
122        $args[0] = &$str;
123        $params  = new ArrayObject($args);
124
125        # --BEHAVIOR-- publicBeforeContentFilter
126        $res = $GLOBALS['core']->callBehavior('publicBeforeContentFilter', $GLOBALS['core'], $tag, $params);
127        $str = $params[0];
128
129        if ($params['strip_tags']) {
130            $str = self::strip_tags($str);
131        }
132        if ($params['remove_html']) {
133            $str = self::remove_html($str);
134            $str = preg_replace('/\s+/', ' ', $str);
135        }
136        if ($params['encode_xml'] || $params['encode_html']) {
137            $str = self::encode_xml($str);
138        }
139
140        if ($params['cut_string'] > 0) {
141            $str = self::cut_string($str, (integer) $params['cut_string']);
142        }
143
144        if ($params['lower_case']) {
145            $str = self::lower_case($str);
146        } elseif ($params['capitalize']) {
147            $str = self::capitalize($str);
148        } elseif ($params['upper_case']) {
149            $str = self::upper_case($str);
150        }
151
152        if ($params['encode_url']) {
153            $str = self::encode_url($str);
154        }
155
156        # --BEHAVIOR-- publicAfterContentFilter
157        $res = $GLOBALS['core']->callBehavior('publicAfterContentFilter', $GLOBALS['core'], $tag, $params);
158        $str = $params[0];
159
160        return $str;
161    }
162
163    public static function encode_url($str)
164    {
165        return urlencode($str);
166    }
167
168    public static function cut_string($str, $l)
169    {
170        return text::cutString($str, $l);
171    }
172
173    public static function encode_xml($str)
174    {
175        return html::escapeHTML($str);
176    }
177
178    public static function remove_html($str)
179    {
180        return html::decodeEntities(html::clean($str));
181    }
182
183    public static function strip_tags($str)
184    {
185        return trim(preg_replace('/ {2,}/', ' ', str_replace(array("\r", "\n", "\t"), ' ', html::clean($str))));
186    }
187
188    public static function lower_case($str)
189    {
190        return mb_strtolower($str);
191    }
192
193    public static function upper_case($str)
194    {
195        return mb_strtoupper($str);
196    }
197
198    public static function capitalize($str)
199    {
200        if ($str != '') {
201            $str[0] = mb_strtoupper($str[0]);
202        }
203        return $str;
204    }
205
206    public static function categoryPostParam(&$p)
207    {
208        $not = substr($p['cat_url'], 0, 1) == '!';
209        if ($not) {
210            $p['cat_url'] = substr($p['cat_url'], 1);
211        }
212
213        $p['cat_url'] = preg_split('/\s*,\s*/', $p['cat_url'], -1, PREG_SPLIT_NO_EMPTY);
214
215        foreach ($p['cat_url'] as &$v) {
216            if ($not) {
217                $v .= ' ?not';
218            }
219            if ($GLOBALS['_ctx']->exists('categories') && preg_match('/#self/', $v)) {
220                $v = preg_replace('/#self/', $GLOBALS['_ctx']->categories->cat_url, $v);
221            } elseif ($GLOBALS['_ctx']->exists('posts') && preg_match('/#self/', $v)) {
222                $v = preg_replace('/#self/', $GLOBALS['_ctx']->posts->cat_url, $v);
223            }
224        }
225    }
226
227    # Static methods for pagination
228    public static function PaginationNbPages()
229    {
230        global $_ctx;
231
232        if ($_ctx->pagination === null) {
233            return false;
234        }
235
236        $nb_posts = $_ctx->pagination->f(0);
237        if (($GLOBALS['core']->url->type == 'default') || ($GLOBALS['core']->url->type == 'default-page')) {
238            $nb_pages = ceil(($nb_posts - $_ctx->nb_entry_first_page) / $_ctx->nb_entry_per_page + 1);
239        } else {
240            $nb_pages = ceil($nb_posts / $_ctx->nb_entry_per_page);
241        }
242
243        return $nb_pages;
244    }
245
246    public static function PaginationPosition($offset = 0)
247    {
248        if (isset($GLOBALS['_page_number'])) {
249            $p = $GLOBALS['_page_number'];
250        } else {
251            $p = 1;
252        }
253
254        $p = $p + $offset;
255
256        $n = self::PaginationNbPages();
257        if (!$n) {
258            return $p;
259        }
260
261        if ($p > $n || $p <= 0) {
262            return 1;
263        } else {
264            return $p;
265        }
266    }
267
268    public static function PaginationStart()
269    {
270        if (isset($GLOBALS['_page_number'])) {
271            return self::PaginationPosition() == 1;
272        }
273
274        return true;
275    }
276
277    public static function PaginationEnd()
278    {
279        if (isset($GLOBALS['_page_number'])) {
280            return self::PaginationPosition() == self::PaginationNbPages();
281        }
282
283        return false;
284    }
285
286    public static function PaginationURL($offset = 0)
287    {
288        $args = $_SERVER['URL_REQUEST_PART'];
289
290        $n = self::PaginationPosition($offset);
291
292        $args = preg_replace('#(^|/)page/([0-9]+)$#', '', $args);
293
294        $url = $GLOBALS['core']->blog->url . $args;
295
296        if ($n > 1) {
297            $url = preg_replace('#/$#', '', $url);
298            $url .= '/page/' . $n;
299        }
300
301        # If search param
302        if (!empty($_GET['q'])) {
303            $s = strpos($url, '?') !== false ? '&amp;' : '?';
304            $url .= $s . 'q=' . rawurlencode($_GET['q']);
305        }
306        return $url;
307    }
308
309    # Robots policy
310    public static function robotsPolicy($base, $over)
311    {
312        $pol  = array('INDEX' => 'INDEX', 'FOLLOW' => 'FOLLOW', 'ARCHIVE' => 'ARCHIVE');
313        $base = array_flip(preg_split('/\s*,\s*/', $base));
314        $over = array_flip(preg_split('/\s*,\s*/', $over));
315
316        foreach ($pol as $k => &$v) {
317            if (isset($base[$k]) || isset($base['NO' . $k])) {
318                $v = isset($base['NO' . $k]) ? 'NO' . $k : $k;
319            }
320            if (isset($over[$k]) || isset($over['NO' . $k])) {
321                $v = isset($over['NO' . $k]) ? 'NO' . $k : $k;
322            }
323        }
324
325        if ($pol['ARCHIVE'] == 'ARCHIVE') {
326            unset($pol['ARCHIVE']);
327        }
328
329        return implode(', ', $pol);
330    }
331
332    # Smilies static methods
333    public static function getSmilies($blog)
334    {
335        $path = array();
336        if (isset($GLOBALS['__theme'])) {
337            $path[] = $GLOBALS['__theme'];
338            if (isset($GLOBALS['__parent_theme'])) {
339                $path[] = $GLOBALS['__parent_theme'];
340            }
341        }
342        $path[]     = 'default';
343        $definition = $blog->themes_path . '/%s/smilies/smilies.txt';
344        $base_url   = $blog->settings->system->themes_url . '/%s/smilies/';
345
346        $res = array();
347
348        foreach ($path as $t) {
349            if (file_exists(sprintf($definition, $t))) {
350                $base_url = sprintf($base_url, $t);
351                return self::smiliesDefinition(sprintf($definition, $t), $base_url);
352            }
353        }
354        return false;
355    }
356
357    public static function smiliesDefinition($f, $url)
358    {
359        $def = file($f);
360
361        $res = array();
362        foreach ($def as $v) {
363            $v = trim($v);
364            if (preg_match('|^([^\t]*)[\t]+(.*)$|', $v, $matches)) {
365                $r = '/(\G|[\s]+|>)(' . preg_quote($matches[1], '/') . ')([\s]+|[<]|\Z)/ms';
366                $s = '$1<img src="' . $url . $matches[2] . '" ' .
367                    'alt="$2" class="smiley" />$3';
368                $res[$r] = $s;
369            }
370        }
371
372        return $res;
373    }
374
375    public static function addSmilies($str)
376    {
377        if (!isset($GLOBALS['__smilies']) || !is_array($GLOBALS['__smilies'])) {
378            return $str;
379        }
380
381        # Process part adapted from SmartyPants engine (J. Gruber et al.) :
382
383        $tokens = self::tokenizeHTML($str);
384        $result = '';
385        $in_pre = 0; # Keep track of when we're inside <pre> or <code> tags.
386
387        foreach ($tokens as $cur_token) {
388            if ($cur_token[0] == "tag") {
389                # Don't mess with quotes inside tags.
390                $result .= $cur_token[1];
391                if (preg_match('@<(/?)(?:pre|code|kbd|script|math)[\s>]@', $cur_token[1], $matches)) {
392                    $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
393                }
394            } else {
395                $t = $cur_token[1];
396                if (!$in_pre) {
397                    $t = preg_replace(array_keys($GLOBALS['__smilies']), array_values($GLOBALS['__smilies']), $t);
398                }
399                $result .= $t;
400            }
401        }
402
403        return $result;
404    }
405
406    private static function tokenizeHTML($str)
407    {
408        # Function from SmartyPants engine (J. Gruber et al.)
409        #
410        #   Parameter:  String containing HTML markup.
411        #   Returns:    An array of the tokens comprising the input
412        #               string. Each token is either a tag (possibly with nested,
413        #               tags contained therein, such as <a href="<MTFoo>">, or a
414        #               run of text between tags. Each element of the array is a
415        #               two-element array; the first is either 'tag' or 'text';
416        #               the second is the actual value.
417        #
418        #
419        #   Regular expression derived from the _tokenize() subroutine in
420        #   Brad Choate's MTRegex plugin.
421        #   <http://www.bradchoate.com/past/mtregex.php>
422        #
423        $index  = 0;
424        $tokens = array();
425
426        $match = '(?s:<!(?:--.*?--\s*)+>)|' . # comment
427        '(?s:<\?.*?\?>)|' . # processing instruction
428        # regular tags
429        '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
430
431        $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
432
433        foreach ($parts as $part) {
434            if (++$index % 2 && $part != '') {
435                $tokens[] = array('text', $part);
436            } else {
437                $tokens[] = array('tag', $part);
438            }
439
440        }
441        return $tokens;
442    }
443
444    # First post image helpers
445    public static function EntryFirstImageHelper($size, $with_category, $class = "", $no_tag = false, $content_only = false, $cat_only = false)
446    {
447        global $core, $_ctx;
448
449        try {
450            $media = new dcMedia($core);
451            $sizes = implode('|', array_keys($media->thumb_sizes)) . '|o';
452            if (!preg_match('/^' . $sizes . '$/', $size)) {
453                $size = 's';
454            }
455            $p_url  = $core->blog->settings->system->public_url;
456            $p_site = preg_replace('#^(.+?//.+?)/(.*)$#', '$1', $core->blog->url);
457            $p_root = $core->blog->public_path;
458
459            $pattern = '(?:' . preg_quote($p_site, '/') . ')?' . preg_quote($p_url, '/');
460            $pattern = sprintf('/<img.+?src="%s(.*?\.(?:jpg|jpeg|gif|png))"[^>]+/msui', $pattern);
461
462            $src = '';
463            $alt = '';
464
465            # We first look in post content
466            if (!$cat_only && $_ctx->posts) {
467                $subject = ($content_only ? '' : $_ctx->posts->post_excerpt_xhtml) . $_ctx->posts->post_content_xhtml;
468                if (preg_match_all($pattern, $subject, $m) > 0) {
469                    foreach ($m[1] as $i => $img) {
470                        if (($src = self::ContentFirstImageLookup($p_root, $img, $size)) !== false) {
471                            $dirname = str_replace('\\', '/', dirname($img));
472                            $src     = $p_url . ($dirname != '/' ? $dirname : '') . '/' . $src;
473                            if (preg_match('/alt="([^"]+)"/', $m[0][$i], $malt)) {
474                                $alt = $malt[1];
475                            }
476                            break;
477                        }
478                    }
479                }
480            }
481
482            # No src, look in category description if available
483            if (!$src && $with_category && $_ctx->posts->cat_desc) {
484                if (preg_match_all($pattern, $_ctx->posts->cat_desc, $m) > 0) {
485                    foreach ($m[1] as $i => $img) {
486                        if (($src = self::ContentFirstImageLookup($p_root, $img, $size)) !== false) {
487                            $dirname = str_replace('\\', '/', dirname($img));
488                            $src     = $p_url . ($dirname != '/' ? $dirname : '') . '/' . $src;
489                            if (preg_match('/alt="([^"]+)"/', $m[0][$i], $malt)) {
490                                $alt = $malt[1];
491                            }
492                            break;
493                        }
494                    }
495                };
496            }
497
498            if ($src) {
499                if ($no_tag) {
500                    return $src;
501                } else {
502                    return '<img alt="' . $alt . '" src="' . $src . '" class="' . $class . '" />';
503                }
504            }
505
506        } catch (Exception $e) {
507            $core->error->add($e->getMessage());
508        }
509    }
510
511    private static function ContentFirstImageLookup($root, $img, $size)
512    {
513        global $core;
514
515        # Get base name and extension
516        $info = path::info($img);
517        $base = $info['base'];
518
519        try {
520            $media = new dcMedia($core);
521            $sizes = implode('|', array_keys($media->thumb_sizes));
522            if (preg_match('/^\.(.+)_(' . $sizes . ')$/', $base, $m)) {
523                $base = $m[1];
524            }
525
526            $res = false;
527            if ($size != 'o' && file_exists($root . '/' . $info['dirname'] . '/.' . $base . '_' . $size . '.jpg')) {
528                $res = '.' . $base . '_' . $size . '.jpg';
529            } elseif ($size != 'o' && file_exists($root . '/' . $info['dirname'] . '/.' . $base . '_' . $size . '.png')) {
530                $res = '.' . $base . '_' . $size . '.png';
531            } else {
532                $f = $root . '/' . $info['dirname'] . '/' . $base;
533                if (file_exists($f . '.' . $info['extension'])) {
534                    $res = $base . '.' . $info['extension'];
535                } elseif (file_exists($f . '.jpg')) {
536                    $res = $base . '.jpg';
537                } elseif (file_exists($f . '.jpeg')) {
538                    $res = $base . '.jpeg';
539                } elseif (file_exists($f . '.png')) {
540                    $res = $base . '.png';
541                } elseif (file_exists($f . '.gif')) {
542                    $res = $base . '.gif';
543                } elseif (file_exists($f . '.JPG')) {
544                    $res = $base . '.JPG';
545                } elseif (file_exists($f . '.JPEG')) {
546                    $res = $base . '.JPEG';
547                } elseif (file_exists($f . '.PNG')) {
548                    $res = $base . '.PNG';
549                } elseif (file_exists($f . '.GIF')) {
550                    $res = $base . '.GIF';
551                }
552            }
553        } catch (Exception $e) {
554            $core->error->add($e->getMessage());
555        }
556
557        if ($res) {
558            return $res;
559        }
560        return false;
561    }
562}
Note: See TracBrowser for help on using the repository browser.

Sites map