root/branches/release-36/php/lib/MTUtil.php @ 2095

Revision 2095, 56.6 kB (checked in by fumiakiy, 19 months ago)

Always pass array type to array_marge for compatibility with PHP5. BugId:69294

  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
3# This program is distributed under the terms of the
4# GNU General Public License, version 2.
5#
6# $Id$
7
8function start_end_ts($ts) {
9    if ($ts) {
10        if (strlen($ts) == 4) {
11            $ts_start = $ts . '0101';
12            $ts_end = $ts . '1231';
13        } elseif (strlen($ts) == 6) {
14            $ts_start = $ts . '01';
15            $ts_end = $ts . sprintf("%02d", days_in(substr($ts, 4, 2), substr($ts, 0, 4)));
16        } else {
17            $ts_start = $ts;
18            $ts_end = $ts;
19        }
20    }
21    return array($ts_start . '000000', $ts_end . '235959');
22}
23
24function start_end_month($ts) {
25    $y = substr($ts, 0, 4);
26    $mo = substr($ts, 4, 2);
27    $start = sprintf("%04d%02d01000000", $y, $mo);
28    $end = sprintf("%04d%02d%02d235959", $y, $mo, days_in($mo, $y));
29    return array($start, $end);
30}
31
32function days_in($m, $y) {
33    return date('t', mktime(0, 0, 0, $m, 1, $y));
34}
35
36function start_end_day($ts) {
37    $day = substr($ts, 0, 8);
38    return array($day . "000000", $day . "235959");
39}
40
41function start_end_year($ts) {
42    $year = substr($ts, 0, 4);
43    return array($year . "0101000000", $year . "1231235959");
44}
45
46function start_end_week($ts) {
47    $y = substr($ts, 0, 4);
48    $mo = substr($ts, 4, 2);
49    $d = substr($ts, 6, 2);
50    $h = substr($ts, 8, 2);
51    $s = substr($ts, 10, 2);
52    $wday = wday_from_ts($y, $mo, $d);
53    list($sd, $sm, $sy) = array($d - $wday, $mo, $y);
54    if ($sd < 1) {
55        $sm--;
56        if ($sm < 1) {
57            $sm = 12; $sy--;
58        }
59        $sd += days_in($sm, $sy);
60    }
61    $start = sprintf("%04d%02d%02d%s", $sy, $sm, $sd, "000000");
62    list($ed, $em, $ey) = array($d + 6 - $wday, $mo, $y);
63    if ($ed > days_in($em, $ey)) {
64        $ed -= days_in($em, $ey);
65        $em++;
66        if ($em > 12) {
67            $em = 1; $ey++;
68        }
69    }
70    $end = sprintf("%04d%02d%02d%s", $ey, $em, $ed, "235959");
71    return array($start, $end);
72}
73
74global $In_Year;
75$In_Year = array(
76    array( 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ),
77    array( 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 ),
78);
79function week2ymd($y, $week) {
80    $jan_one_dow_m1 = (ymd2rd($y, 1, 1) + 6) % 7;
81
82    if ($jan_one_dow_m1 < 4) $week--;
83    $day_of_year = $week * 7 - $jan_one_dow_m1;
84    $leap_year = is_leap_year($y);
85    if ($day_of_year < 1) {
86        $y--;
87        $day_of_year = ($leap_year ? 366 : 365) + $day_of_year;
88    }
89    if ($leap_year) {
90        $ref = array(0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335);
91    } else {
92        $ref = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
93    }
94    $m = 0;
95    for ($i = count($ref); $i > 0; $i--) {
96        if ($day_of_year > $ref[$i-1]) {
97            $m = $i;
98            break;
99        }
100    }
101    return array($y, $m, $day_of_year - $ref[$m-1]);
102}
103
104function is_leap_year($y) {
105    return (!($y % 4) && ($y % 100)) || !($y % 400) ? true : false;
106}
107
108function ymd2rd($y,$m,$d) {
109    # make month in range 3..14 (treat Jan & Feb as months 13..14 of
110    # prev year)
111    if ( $m <= 2 ) {
112        $adj = (int)(( 14 - $m ) / 12);
113        $y -= $adj;
114        $m += 12 * $adj;
115    }
116    elseif ( $m > 14 )
117    {
118        $adj = (int)(( $m - 3 ) / 12);
119        $y += $adj;
120        $m -= 12 * $adj;
121    }
122
123    # make year positive (oh, for a use integer 'sane_div'!)
124    if ( $y < 0 )
125    {
126        $adj = (int)(( 399 - $y ) / 400);
127        $d -= 146097 * $adj; 
128        $y += 400 * $adj;
129    }
130
131    # add: day of month, days of previous 0-11 month period that began
132    # w/March, days of previous 0-399 year period that began w/March
133    # of a 400-multiple year), days of any 400-year periods before
134    # that, and 306 days to adjust from Mar 1, year 0-relative to Jan
135    # 1, year 1-relative (whew)
136    $d += (int)(( $m * 367 - 1094 ) / 12) + (int)((($y % 100) * 1461) / 4) +
137          ( (int)($y / 100) * 36524 + (int)($y / 400) ) - 306;
138    return $d;
139}
140
141function wday_from_ts($y, $m, $d) {
142    global $In_Year;
143    $leap = $y % 4 == 0 && ($y % 100 != 0 || $y % 400 == 0) ? 1 : 0;
144    $y--;
145
146    ## Copied from Date::Calc.
147    $days = $y * 365;
148    $days += $y >>= 2;
149    $days -= intval($y /= 25);
150    $days += $y >> 2;
151    $days += $In_Year[$leap][$m-1] + $d;
152    return $days % 7;
153}
154
155function yday_from_ts($y, $m, $d) {
156    global $In_Year;
157    $leap = $y % 4 == 0 && ($y % 100 != 0 || $y % 400 == 0) ? 1 : 0;
158    return $In_Year[$leap][$m-1] + $d;
159}
160
161function substr_wref($str, $start, $length) {
162    if (preg_match_all('/(&[^;]*;|.)/', $str, $character_entities)) {
163        return implode('', array_slice($character_entities[0], $start, $length));
164    } else {
165        return '';
166    }
167}
168
169function format_ts($format, $ts, $blog, $lang = null) {
170    global $Languages;
171    if (!isset($lang) || empty($lang)) { 
172        global $mt;
173        $lang = ($blog && $blog['blog_language'] ? $blog['blog_language'] : 
174                     $mt->config('DefaultLanguage'));
175    }
176    if ($lang == 'jp') {
177        $lang = 'ja';
178    }
179    $lang = strtolower(substr($lang, 0, 2));
180    if (!isset($format) || empty($format)) {
181        if (count($Languages[$lang]) >= 4)
182            $format = $Languages[$lang][3];
183        $format or $format = "%B %e, %Y %l:%M %p";
184    }
185    global $_format_ts_cache;
186    if (!isset($_format_ts_cache)) {
187        $_format_ts_cache = array();
188    }   
189    if (isset($_format_ts_cache[$ts.$lang])) {
190        $f = $_format_ts_cache[$ts.$lang];
191    } else {
192        $L = $Languages[$lang];
193        $tsa = array(substr($ts, 0, 4), substr($ts, 4, 2), substr($ts, 6, 2),
194                     substr($ts, 8, 2), substr($ts, 10, 2), substr($ts, 12, 2));
195        list($f['Y'], $f['m'], $f['d'], $f['H'], $f['M'], $f['S']) = $tsa;
196        $f['w'] = wday_from_ts($tsa[0],$tsa[1],$tsa[2]);
197        $f['j'] = yday_from_ts($tsa[0],$tsa[1],$tsa[2]);
198        $f['y'] = substr($f['Y'], 2);
199        $f['b'] = substr_wref($L[1][$f['m']-1], 0, 3);
200        $f['B'] = $L[1][$f['m']-1];
201        if ($lang == 'ja') {
202            $f['a'] = substr($L[0][$f['w']], 0, 8);
203        } else {
204            $f['a'] = substr_wref($L[0][$f['w']], 0, 3);
205        }
206        $f['A'] = $L[0][$f['w']];
207        $f['e'] = $f['d'];
208        $f['e'] = preg_replace('!^0!', ' ', $f['e']);
209        $f['I'] = $f['H'];
210        if ($f['I'] > 12) {
211            $f['I'] -= 12;
212            $f['p'] = $L[2][1];
213        } elseif ($f['I'] == 0) {
214            $f['I'] = 12;
215            $f['p'] = $L[2][0];
216        } elseif ($f['I'] == 12) {
217            $f['p'] = $L[2][1];
218        } else {
219            $f['p'] = $L[2][0];
220        }
221        $f['I'] = sprintf("%02d", $f['I']);
222        $f['k'] = $f['H'];
223        $f['k'] = preg_replace('!^0!', ' ', $f['k']);
224        $f['l'] = $f['I'];
225        $f['l'] = preg_replace('!^0!', ' ', $f['l']);
226        $f['j'] = sprintf("%03d", $f['j']);
227        $f['Z'] = '';
228        $_format_ts_cache[$ts . $lang] = $f;
229    }
230    $date_format = null;
231    if (count($Languages[$lang]) >= 5)
232        $date_format = $Languages[$lang][4];
233    $date_format or $date_format = "%B %e, %Y";
234    $time_format = null;
235    if (count($Languages[$lang]) >= 6)
236        $time_format = $Languages[$lang][5];
237    $time_format or $time_format = "%l:%M %p";
238    $format = preg_replace('!%x!', $date_format, $format);
239    $format = preg_replace('!%X!', $time_format, $format);
240    ## This is a dreadful hack. I can't think of a good format specifier
241    ## for "%B %Y" (which is used for monthly archives, for example) so
242    ## I'll just hardcode this, for Japanese dates.
243    if ($lang == 'ja') {
244        if (count($Languages[$lang]) >= 8) {
245            $format = preg_replace('!%B %Y!', $Languages[$lang][6], $format);
246            $format = preg_replace('!%B %E,? %Y!i', $Languages[$lang][4], $format);
247            $format = preg_replace('!%B %E!', $Languages[$lang][7], $format);
248        }
249    }
250    if (isset($format)) {
251        $format = preg_replace('!%(\w)!e', '\$f[\'\1\']', $format);
252    }
253    return $format;
254}
255
256function dirify($s, $sep = '_') {
257    global $mt;
258    $charset = $mt->config('PublishCharset');
259    $charset or $charset = 'utf-8';
260    if (preg_match('/utf-?8/i', $charset)) {
261        return utf8_dirify($s, $sep);
262    } else {
263        return iso_dirify($s, $sep);
264    }
265}
266
267function utf8_dirify($s, $sep = '_') {
268    if ($sep == '1') $sep = '_';
269    $s = xliterate_utf8($s);  ## convert high-ASCII chars to 7bit.
270    $s = strtolower($s);                   ## lower-case.
271    $s = strip_tags($s);          ## remove HTML tags.
272    $s = preg_replace('!&[^;\s]+;!', '', $s); ## remove HTML entities.
273    $s = preg_replace('![^\w\s]!', '', $s);   ## remove non-word/space chars.
274    $s = preg_replace('/\s+/',$sep,$s);         ## change space chars to underscores.
275    return($s);
276}
277
278global $Utf8_ASCII;
279$Utf8_ASCII = array(
280    "\xc3\x80" => 'A',    # A`
281    "\xc3\xa0" => 'a',    # a`
282    "\xc3\x81" => 'A',    # A'
283    "\xc3\xa1" => 'a',    # a'
284    "\xc3\x82" => 'A',    # A^
285    "\xc3\xa2" => 'a',    # a^
286    "\xc4\x82" => 'A',    # latin capital letter a with breve
287    "\xc4\x83" => 'a',    # latin small letter a with breve
288    "\xc3\x86" => 'AE',   # latin capital letter AE
289    "\xc3\xa6" => 'ae',   # latin small letter ae
290    "\xc3\x85" => 'A',    # latin capital letter a with ring above
291    "\xc3\xa5" => 'a',    # latin small letter a with ring above
292    "\xc4\x80" => 'A',    # latin capital letter a with macron
293    "\xc4\x81" => 'a',    # latin small letter a with macron
294    "\xc4\x84" => 'A',    # latin capital letter a with ogonek
295    "\xc4\x85" => 'a',    # latin small letter a with ogonek
296    "\xc3\x84" => 'A',    # A:
297    "\xc3\xa4" => 'a',    # a:
298    "\xc3\x83" => 'A',    # A~
299    "\xc3\xa3" => 'a',    # a~
300    "\xc3\x88" => 'E',    # E`
301    "\xc3\xa8" => 'e',    # e`
302    "\xc3\x89" => 'E',    # E'
303    "\xc3\xa9" => 'e',    # e'
304    "\xc3\x8a" => 'E',    # E^
305    "\xc3\xaa" => 'e',    # e^
306    "\xc3\x8b" => 'E',    # E:
307    "\xc3\xab" => 'e',    # e:
308    "\xc4\x92" => 'E',    # latin capital letter e with macron
309    "\xc4\x93" => 'e',    # latin small letter e with macron
310    "\xc4\x98" => 'E',    # latin capital letter e with ogonek
311    "\xc4\x99" => 'e',    # latin small letter e with ogonek
312    "\xc4\x9a" => 'E',    # latin capital letter e with caron
313    "\xc4\x9b" => 'e',    # latin small letter e with caron
314    "\xc4\x94" => 'E',    # latin capital letter e with breve
315    "\xc4\x95" => 'e',    # latin small letter e with breve
316    "\xc4\x96" => 'E',    # latin capital letter e with dot above
317    "\xc4\x97" => 'e',    # latin small letter e with dot above
318    "\xc3\x8c" => 'I',    # I`
319    "\xc3\xac" => 'i',    # i`
320    "\xc3\x8d" => 'I',    # I'
321    "\xc3\xad" => 'i',    # i'
322    "\xc3\x8e" => 'I',    # I^
323    "\xc3\xae" => 'i',    # i^
324    "\xc3\x8f" => 'I',    # I:
325    "\xc3\xaf" => 'i',    # i:
326    "\xc4\xaa" => 'I',    # latin capital letter i with macron
327    "\xc4\xab" => 'i',    # latin small letter i with macron
328    "\xc4\xa8" => 'I',    # latin capital letter i with tilde
329    "\xc4\xa9" => 'i',    # latin small letter i with tilde
330    "\xc4\xac" => 'I',    # latin capital letter i with breve
331    "\xc4\xad" => 'i',    # latin small letter i with breve
332    "\xc4\xae" => 'I',    # latin capital letter i with ogonek
333    "\xc4\xaf" => 'i',    # latin small letter i with ogonek
334    "\xc4\xb0" => 'I',    # latin capital letter with dot above
335    "\xc4\xb1" => 'i',    # latin small letter dotless i
336    "\xc4\xb2" => 'IJ',   # latin capital ligature ij
337    "\xc4\xb3" => 'ij',   # latin small ligature ij
338    "\xc4\xb4" => 'J',    # latin capital letter j with circumflex
339    "\xc4\xb5" => 'j',    # latin small letter j with circumflex
340    "\xc4\xb6" => 'K',    # latin capital letter k with cedilla
341    "\xc4\xb7" => 'k',    # latin small letter k with cedilla
342    "\xc4\xb8" => 'k',    # latin small letter kra
343    "\xc5\x81" => 'L',    # latin capital letter l with stroke
344    "\xc5\x82" => 'l',    # latin small letter l with stroke
345    "\xc4\xbd" => 'L',    # latin capital letter l with caron
346    "\xc4\xbe" => 'l',    # latin small letter l with caron
347    "\xc4\xb9" => 'L',    # latin capital letter l with acute
348    "\xc4\xba" => 'l',    # latin small letter l with acute
349    "\xc4\xbb" => 'L',    # latin capital letter l with cedilla
350    "\xc4\xbc" => 'l',    # latin small letter l with cedilla
351    "\xc4\xbf" => 'l',    # latin capital letter l with middle dot
352    "\xc5\x80" => 'l',    # latin small letter l with middle dot
353    "\xc3\x92" => 'O',    # O`
354    "\xc3\xb2" => 'o',    # o`
355    "\xc3\x93" => 'O',    # O'
356    "\xc3\xb3" => 'o',    # o'
357    "\xc3\x94" => 'O',    # O^
358    "\xc3\xb4" => 'o',    # o^
359    "\xc3\x96" => 'O',    # O:
360    "\xc3\xb6" => 'o',    # o:
361    "\xc3\x95" => 'O',    # O~
362    "\xc3\xb5" => 'o',    # o~
363    "\xc3\x98" => 'O',    # O/
364    "\xc3\xb8" => 'o',    # o/
365    "\xc5\x8c" => 'O',    # latin capital letter o with macron
366    "\xc5\x8d" => 'o',    # latin small letter o with macron
367    "\xc5\x90" => 'O',    # latin capital letter o with double acute
368    "\xc5\x91" => 'o',    # latin small letter o with double acute
369    "\xc5\x8e" => 'O',    # latin capital letter o with breve
370    "\xc5\x8f" => 'o',    # latin small letter o with breve
371    "\xc5\x92" => 'OE',   # latin capital ligature oe
372    "\xc5\x93" => 'oe',   # latin small ligature oe
373    "\xc5\x94" => 'R',    # latin capital letter r with acute
374    "\xc5\x95" => 'r',    # latin small letter r with acute
375    "\xc5\x98" => 'R',    # latin capital letter r with caron
376    "\xc5\x99" => 'r',    # latin small letter r with caron
377    "\xc5\x96" => 'R',    # latin capital letter r with cedilla
378    "\xc5\x97" => 'r',    # latin small letter r with cedilla
379    "\xc3\x99" => 'U',    # U`
380    "\xc3\xb9" => 'u',    # u`
381    "\xc3\x9a" => 'U',    # U'
382    "\xc3\xba" => 'u',    # u'
383    "\xc3\x9b" => 'U',    # U^
384    "\xc3\xbb" => 'u',    # u^
385    "\xc3\x9c" => 'U',    # U:
386    "\xc3\xbc" => 'u',    # u:
387    "\xc5\xaa" => 'U',    # latin capital letter u with macron
388    "\xc5\xab" => 'u',    # latin small letter u with macron
389    "\xc5\xae" => 'U',    # latin capital letter u with ring above
390    "\xc5\xaf" => 'u',    # latin small letter u with ring above
391    "\xc5\xb0" => 'U',    # latin capital letter u with double acute
392    "\xc5\xb1" => 'u',    # latin small letter u with double acute
393    "\xc5\xac" => 'U',    # latin capital letter u with breve
394    "\xc5\xad" => 'u',    # latin small letter u with breve
395    "\xc5\xa8" => 'U',    # latin capital letter u with tilde
396    "\xc5\xa9" => 'u',    # latin small letter u with tilde
397    "\xc5\xb2" => 'U',    # latin capital letter u with ogonek
398    "\xc5\xb3" => 'u',    # latin small letter u with ogonek
399    "\xc3\x87" => 'C',    # ,C
400    "\xc3\xa7" => 'c',    # ,c
401    "\xc4\x86" => 'C',    # latin capital letter c with acute
402    "\xc4\x87" => 'c',    # latin small letter c with acute
403    "\xc4\x8c" => 'C',    # latin capital letter c with caron
404    "\xc4\x8d" => 'c',    # latin small letter c with caron
405    "\xc4\x88" => 'C',    # latin capital letter c with circumflex
406    "\xc4\x89" => 'c',    # latin small letter c with circumflex
407    "\xc4\x8a" => 'C',    # latin capital letter c with dot above
408    "\xc4\x8b" => 'c',    # latin small letter c with dot above
409    "\xc4\x8e" => 'D',    # latin capital letter d with caron
410    "\xc4\x8f" => 'd',    # latin small letter d with caron
411    "\xc4\x90" => 'D',    # latin capital letter d with stroke
412    "\xc4\x91" => 'd',    # latin small letter d with stroke
413    "\xc3\x91" => 'N',    # N~
414    "\xc3\xb1" => 'n',    # n~
415    "\xc5\x83" => 'N',    # latin capital letter n with acute
416    "\xc5\x84" => 'n',    # latin small letter n with acute
417    "\xc5\x87" => 'N',    # latin capital letter n with caron
418    "\xc5\x88" => 'n',    # latin small letter n with caron
419    "\xc5\x85" => 'N',    # latin capital letter n with cedilla
420    "\xc5\x86" => 'n',    # latin small letter n with cedilla
421    "\xc5\x89" => 'n',    # latin small letter n preceded by apostrophe
422    "\xc5\x8a" => 'N',    # latin capital letter eng
423    "\xc5\x8b" => 'n',    # latin small letter eng
424    "\xc3\x9f" => 'ss',   # double-s
425    "\xc5\x9a" => 'S',    # latin capital letter s with acute
426    "\xc5\x9b" => 's',    # latin small letter s with acute
427    "\xc5\xa0" => 'S',    # latin capital letter s with caron
428    "\xc5\xa1" => 's',    # latin small letter s with caron
429    "\xc5\x9e" => 'S',    # latin capital letter s with cedilla
430    "\xc5\x9f" => 's',    # latin small letter s with cedilla
431    "\xc5\x9c" => 'S',    # latin capital letter s with circumflex
432    "\xc5\x9d" => 's',    # latin small letter s with circumflex
433    "\xc8\x98" => 'S',    # latin capital letter s with comma below
434    "\xc8\x99" => 's',    # latin small letter s with comma below
435    "\xc5\xa4" => 'T',    # latin capital letter t with caron
436    "\xc5\xa5" => 't',    # latin small letter t with caron
437    "\xc5\xa2" => 'T',    # latin capital letter t with cedilla
438    "\xc5\xa3" => 't',    # latin small letter t with cedilla
439    "\xc5\xa6" => 'T',    # latin capital letter t with stroke
440    "\xc5\xa7" => 't',    # latin small letter t with stroke
441    "\xc8\x9a" => 'T',    # latin capital letter t with comma below
442    "\xc8\x9b" => 't',    # latin small letter t with comma below
443    "\xc6\x92" => 'f',    # latin small letter f with hook
444    "\xc4\x9c" => 'G',    # latin capital letter g with circumflex
445    "\xc4\x9d" => 'g',    # latin small letter g with circumflex
446    "\xc4\x9e" => 'G',    # latin capital letter g with breve
447    "\xc4\x9f" => 'g',    # latin small letter g with breve
448    "\xc4\xa0" => 'G',    # latin capital letter g with dot above
449    "\xc4\xa1" => 'g',    # latin small letter g with dot above
450    "\xc4\xa2" => 'G',    # latin capital letter g with cedilla
451    "\xc4\xa3" => 'g',    # latin small letter g with cedilla
452    "\xc4\xa4" => 'H',    # latin capital letter h with circumflex
453    "\xc4\xa5" => 'h',    # latin small letter h with circumflex
454    "\xc4\xa6" => 'H',    # latin capital letter h with stroke
455    "\xc4\xa7" => 'h',    # latin small letter h with stroke
456    "\xc5\xb4" => 'W',    # latin capital letter w with circumflex
457    "\xc5\xb5" => 'w',    # latin small letter w with circumflex
458    "\xc3\x9d" => 'Y',    # latin capital letter y with acute
459    "\xc3\xbd" => 'y',    # latin small letter y with acute
460    "\xc5\xb8" => 'Y',    # latin capital letter y with diaeresis
461    "\xc3\xbf" => 'y',    # latin small letter y with diaeresis
462    "\xc5\xb6" => 'Y',    # latin capital letter y with circumflex
463    "\xc5\xb7" => 'y',    # latin small letter y with circumflex
464    "\xc5\xbd" => 'Z',    # latin capital letter z with caron
465    "\xc5\xbe" => 'z',    # latin small letter z with caron
466    "\xc5\xbb" => 'Z',    # latin capital letter z with dot above
467    "\xc5\xbc" => 'z',    # latin small letter z with dot above
468    "\xc5\xb9" => 'Z',    # latin capital letter z with acute
469    "\xc5\xba" => 'z',    # latin small letter z with acute
470);
471function xliterate_utf8($s) {
472    global $Utf8_ASCII;
473    return strtr($s, $Utf8_ASCII);
474}
475
476function iso_dirify($s, $sep = '_') {
477    if ($sep == '1') $sep = '_';
478    $s = convert_high_ascii($s);  ## convert high-ASCII chars to 7bit.
479    $s = strtolower($s);                   ## lower-case.
480    $s = strip_tags($s);          ## remove HTML tags.
481    $s = preg_replace('!&[^;\s]+;!', '', $s); ## remove HTML entities.
482    $s = preg_replace('![^\w\s]!', '', $s);   ## remove non-word/space chars.
483    $s = preg_replace('/\s+/',$sep,$s);         ## change space chars to underscores.
484    return($s);
485}
486
487global $Latin1_ASCII;
488$Latin1_ASCII = array(
489    "\xc0" => 'A',    # A`
490    "\xe0" => 'a',    # a`
491    "\xc1" => 'A',    # A'
492    "\xe1" => 'a',    # a'
493    "\xc2" => 'A',    # A^
494    "\xe2" => 'a',    # a^
495    "\xc4" => 'A',    # A:
496    "\xe4" => 'a',    # a:
497    "\xc5" => 'A',    # Aring
498    "\xe5" => 'a',    # aring
499    "\xc6" => 'AE',   # AE
500    "\xe6" => 'ae',   # ae
501    "\xc3" => 'A',    # A~
502    "\xe3" => 'a',    # a~
503    "\xc8" => 'E',    # E`
504    "\xe8" => 'e',    # e`
505    "\xc9" => 'E',    # E'
506    "\xe9" => 'e',    # e'
507    "\xca" => 'E',    # E^
508    "\xea" => 'e',    # e^
509    "\xcb" => 'E',    # E:
510    "\xeb" => 'e',    # e:
511    "\xcc" => 'I',    # I`
512    "\xec" => 'i',    # i`
513    "\xcd" => 'I',    # I'
514    "\xed" => 'i',    # i'
515    "\xce" => 'I',    # I^
516    "\xee" => 'i',    # i^
517    "\xcf" => 'I',    # I:
518    "\xef" => 'i',    # i:
519    "\xd2" => 'O',    # O`
520    "\xf2" => 'o',    # o`
521    "\xd3" => 'O',    # O'
522    "\xf3" => 'o',    # o'
523    "\xd4" => 'O',    # O^
524    "\xf4" => 'o',    # o^
525    "\xd6" => 'O',    # O:
526    "\xf6" => 'o',    # o:
527    "\xd5" => 'O',    # O~
528    "\xf5" => 'o',    # o~
529    "\xd8" => 'O',    # O/
530    "\xf8" => 'o',    # o/
531    "\xd9" => 'U',    # U`
532    "\xf9" => 'u',    # u`
533    "\xda" => 'U',    # U'
534    "\xfa" => 'u',    # u'
535    "\xdb" => 'U',    # U^
536    "\xfb" => 'u',    # u^
537    "\xdc" => 'U',    # U:
538    "\xfc" => 'u',    # u:
539    "\xc7" => 'C',    # ,C
540    "\xe7" => 'c',    # ,c
541    "\xd1" => 'N',    # N~
542    "\xf1" => 'n',    # n~
543    "\xdd" => 'Y',    # Yacute
544    "\xfd" => 'y',    # yacute
545    "\xdf" => 'ss',   # szlig
546    "\xff" => 'y'     # yuml
547);
548function convert_high_ascii($s) {
549    global $mt;
550    $lang = $mt->config('DefaultLanguage');
551    if ($lang == 'ja') {
552        $s = mb_convert_encoding($s, 'UTF-8');
553        return $s;
554    }
555    global $Latin1_ASCII;
556    return strtr($s, $Latin1_ASCII);
557}
558
559global $Languages;
560$Languages = array(
561    'en' => array(
562            array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'),
563            array('January','February','March','April','May','June',
564                  'July','August','September','October','November','December'),
565            array('AM','PM'),
566          ),
567
568    'fr' => array(
569            array('dimanche','lundi','mardi','mercredi','jeudi','vendredi','samedi' ),
570            array('janvier', "f&#xe9;vrier", 'mars', 'avril', 'mai', 'juin',
571               'juillet', "ao&#xfb;t", 'septembre', 'octobre', 'novembre',
572               "d&#xe9;cembre"),
573            array('AM','PM'),
574          ),
575
576    'es' => array(
577            array('Domingo', 'Lunes', 'Martes', "Mi&#xe9;rcoles", 'Jueves',
578               'Viernes', "S&#xe1;bado"),
579            array('Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto',
580                  'Septiembre','Octubre','Noviembre','Diciembre'),
581            array('AM','PM'),
582          ),
583
584    'pt' => array(
585            array('domingo', 'segunda-feira', "ter&#xe7;a-feira", 'quarta-feira',
586               'quinta-feira', 'sexta-feira', "s&#xe1;bado"),
587            array('janeiro', 'fevereiro', "mar&#xe7;o", 'abril', 'maio', 'junho',
588               'julho', 'agosto', 'setembro', 'outubro', 'novembro',
589               'dezembro' ),
590            array('AM','PM'),
591          ),
592
593    'nl' => array(
594            array('zondag','maandag','dinsdag','woensdag','donderdag','vrijdag',
595                  'zaterdag'),
596            array('januari','februari','maart','april','mei','juni','juli','augustus',
597                  'september','oktober','november','december'),
598            array('am','pm'),
599             "%d %B %Y %H:%M",
600             "%d %B %Y"
601          ),
602
603    'dk' => array(
604            array("s&#xf8;ndag", 'mandag', 'tirsdag', 'onsdag', 'torsdag',
605               'fredag', "l&#xf8;rdag"),
606            array('januar','februar','marts','april','maj','juni','juli','august',
607                  'september','oktober','november','december'),
608            array('am','pm'),
609            "%d.%m.%Y %H:%M",
610            "%d.%m.%Y",
611            "%H:%M",
612          ),
613
614    'se' => array(
615            array("s&#xf6;ndag", "m&#xe5;ndag", 'tisdag', 'onsdag', 'torsdag',
616               'fredag', "l&#xf6;rdag"),
617            array('januari','februari','mars','april','maj','juni','juli','augusti',
618                  'september','oktober','november','december'),
619            array('FM','EM'),
620          ),
621
622    'no' => array(
623            array("S&#xf8;ndag", "Mandag", 'Tirsdag', 'Onsdag', 'Torsdag',
624               'Fredag', "L&#xf8;rdag"),
625            array('Januar','Februar','Mars','April','Mai','Juni','Juli','August',
626                  'September','Oktober','November','Desember'),
627            array('FM','EM'),
628          ),
629
630    'de' => array(
631            array('Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag',
632                  'Samstag'),
633            array('Januar', 'Februar', "M&#xe4;rz", 'April', 'Mai', 'Juni',
634               'Juli', 'August', 'September', 'Oktober', 'November',
635               'Dezember'),
636            array('FM','EM'),
637            "%d.%m.%y %H:%M",
638            "%d.%m.%y",
639            "%H:%M",
640          ),
641
642    'it' => array(
643            array('Domenica', "Luned&#xec;", "Marted&#xec;", "Mercoled&#xec;",
644               "Gioved&#xec;", "Venerd&#xec;", 'Sabato'),
645            array('Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio',
646                  'Agosto','Settembre','Ottobre','Novembre','Dicembre'),
647            array('AM','PM'),
648            "%d.%m.%y %H:%M",
649            "%d.%m.%y",
650            "%H:%M",
651          ),
652
653    'pl' => array(
654            array('niedziela', "poniedzia&#322;ek", 'wtorek', "&#347;roda",
655               'czwartek', "pi&#261;tek", 'sobota'),
656            array('stycznia', 'lutego', 'marca', 'kwietnia', 'maja', 'czerwca',
657               'lipca', 'sierpnia', "wrze&#347;nia", "pa&#378;dziernika",
658               'listopada', 'grudnia'),
659            array('AM','PM'),
660            "%e %B %Y %k:%M",
661            "%e %B %Y",
662            "%k:%M",
663          ),
664           
665    'fi' => array(
666            array('sunnuntai','maanantai','tiistai','keskiviikko','torstai','perjantai',
667                  'lauantai'),
668            array('tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu',
669               "kes&#xe4;kuu", "hein&#xe4;kuu", 'elokuu', 'syyskuu', 'lokakuu',
670               'marraskuu', 'joulukuu'),
671            array('AM','PM'),
672            "%d.%m.%y %H:%M",
673          ),
674           
675    'is' => array(
676            array('Sunnudagur', "M&#xe1;nudagur", "&#xde;ri&#xf0;judagur",
677               "Mi&#xf0;vikudagur", 'Fimmtudagur', "F&#xf6;studagur",
678               'Laugardagur'),
679            array("jan&#xfa;ar", "febr&#xfa;ar", 'mars', "apr&#xed;l", "ma&#xed;",
680               "j&#xfa;n&#xed;", "j&#xfa;l&#xed;", "&#xe1;g&#xfa;st", 'september',             
681               "okt&#xf3;ber", "n&#xf3;vember", 'desember'),
682            array('FH','EH'),
683            "%d.%m.%y %H:%M",
684          ),
685           
686    'si' => array(
687            array('nedelja', 'ponedeljek', 'torek', 'sreda', "&#xe3;etrtek",
688               'petek', 'sobota'),
689            array('januar','februar','marec','april','maj','junij','julij','avgust',
690                  'september','oktober','november','december'),
691            array('AM','PM'),
692            "%d.%m.%y %H:%M",
693          ),
694           
695    'cz' => array(
696            array('Ned&#283;le', 'Pond&#283;l&#237;', '&#218;ter&#253;',
697               'St&#345;eda', '&#268;tvrtek', 'P&#225;tek', 'Sobota'),
698            array('Leden', '&#218;nor', 'B&#345;ezen', 'Duben', 'Kv&#283;ten',
699               '&#268;erven', '&#268;ervenec', 'Srpen', 'Z&#225;&#345;&#237;',
700               '&#216;&#237;jen', 'Listopad', 'Prosinec'),
701            array('AM','PM'),
702            "%e. %B %Y %k:%M",
703            "%e. %B %Y",
704            "%k:%M",
705          ),
706           
707    'sk' => array(
708            array('nede&#318;a', 'pondelok', 'utorok', 'streda',
709               '&#353;tvrtok', 'piatok', 'sobota'),
710            array('janu&#225;r', 'febru&#225;r', 'marec', 'apr&#237;l',
711               'm&#225;j', 'j&#250;n', 'j&#250;l', 'august', 'september',
712               'okt&#243;ber', 'november', 'december'),
713            array('AM','PM'),
714            "%e. %B %Y %k:%M",
715            "%e. %B %Y",
716            "%k:%M",
717          ),
718
719    'jp' => array(
720            array('&#26085;&#26332;&#26085;', '&#26376;&#26332;&#26085;',
721              '&#28779;&#26332;&#26085;', '&#27700;&#26332;&#26085;',
722              '&#26408;&#26332;&#26085;', '&#37329;&#26332;&#26085;',
723              '&#22303;&#26332;&#26085;'),
724            array('1','2','3','4','5','6','7','8','9','10','11','12'),
725            array('AM','PM'),
726            "%Y&#24180;%b&#26376;%e&#26085; %H:%M",
727            "%Y&#24180;%b&#26376;%e&#26085;",
728            "%H:%M",
729            "%Y&#24180;%b&#26376;",
730            "%b&#26376;%e&#26085;",
731          ),
732
733    'ja' => array(
734            array('&#26085;&#26332;&#26085;', '&#26376;&#26332;&#26085;',
735              '&#28779;&#26332;&#26085;', '&#27700;&#26332;&#26085;',
736              '&#26408;&#26332;&#26085;', '&#37329;&#26332;&#26085;',
737              '&#22303;&#26332;&#26085;'),
738            array('1','2','3','4','5','6','7','8','9','10','11','12'),
739            array('AM','PM'),
740            "%Y&#24180;%b&#26376;%e&#26085; %H:%M",
741            "%Y&#24180;%b&#26376;%e&#26085;",
742            "%H:%M",
743            "%Y&#24180;%b&#26376;",
744            "%b&#26376;%e&#26085;",
745          ),
746
747    'et' => array(
748            array('ip&uuml;hap&auml;ev','esmasp&auml;ev','teisip&auml;ev',
749                  'kolmap&auml;ev','neljap&auml;ev','reede','laup&auml;ev'),
750            array('jaanuar', 'veebruar', 'm&auml;rts', 'aprill', 'mai',
751               'juuni', 'juuli', 'august', 'september', 'oktoober',
752              'november', 'detsember'),
753            array('AM','PM'),
754            "%m.%d.%y %H:%M",
755            "%e. %B %Y",
756            "%H:%M",
757          ),
758);
759
760global $_encode_xml_Map;
761$_encode_xml_Map = array('&' => '&amp;', '"' => '&quot;',
762                         '<' => '&lt;', '>' => '&gt;',
763                         '\'' => '&apos;');
764
765function encode_xml($str, $nocdata = 0) {
766    global $mt, $_encode_xml_Map;
767    $cfg_nocdata = $mt->config('NoCDATA');
768    $nocdata or (isset($cfg_nocdata) and $nocdata = $cfg_nocdata);
769    if ((!$nocdata) && (preg_match('/
770        <[^>]+>  ## HTML markup
771        |        ## or
772        &(?:(?!(\#([0-9]+)|\#x([0-9a-fA-F]+))).*?);
773                 ## something that looks like an HTML entity.
774        /x', $str))) {
775        ## If ]]> exists in the string, encode the > to &gt;.
776        $str = preg_replace('/]]>/', ']]&gt;', $str);
777        $str = '<![CDATA[' . $str . ']]>';
778    } else {
779        $str = strtr($str, $_encode_xml_Map);
780        $str = preg_replace('/&amp;((\#([0-9]+)|\#x([0-9a-fA-F]+)).*?);/', "&$1;", $str);
781    }
782    return $str;
783}
784
785function decode_xml($str) {
786    if (preg_match('/<!\[CDATA\[(.*?)]]>/', $str)) {
787        $str = preg_replace('/<!\[CDATA\[(.*?)]]>/', '\1', $str);
788        ## Decode encoded ]]&gt;
789        $str = preg_replace('/]]&(gt|#62);/', ']]>', $str);
790    } else {
791        global $_encode_xml_Map;
792        $str = strtr($str, array_flip($_encode_xml_Map));
793    }
794    return $str;
795}
796
797function encode_js($str) {
798    if (!isset($str)) return '';
799    $str = preg_replace('!\\\\!', '\\\\', $str);
800    $str = preg_replace('!>!', '\\>', $str);
801    $str = preg_replace('!<!', '\\<', $str);
802    $str = preg_replace('!(s)(cript)!i', '$1\\\\$2', $str);
803    $str = preg_replace('!</!', '<\\/', $str); # </ is supposed to be the end of Javascript (</script in most UA)
804    $str = preg_replace('!\'!', '\\\'', $str);
805    $str = preg_replace('!"!', '\\"', $str);
806    $str = preg_replace('!\n!', '\\n', $str);
807    $str = preg_replace('!\f!', '\\f', $str);
808    $str = preg_replace('!\r!', '\\r', $str);
809    $str = preg_replace('!\t!', '\\t', $str);
810    return $str;
811}
812
813function gmtime($ts = null) {
814    if (!isset($ts)) {
815        $ts = time();
816    }
817    $offset = date('Z', $ts);
818    $ts -= $offset;
819    $tsa = localtime($ts);
820    $tsa[8] = 0;
821    return $tsa;
822}
823
824function is_hash($array) {
825    if ( is_array($array) ) {
826        if ( array_keys($array) === range(0, count($array) - 1) ) {
827            // 0,1,2,3... must be an array
828            return false;
829        }
830        return true;
831    }
832    return false;
833}
834
835function offset_time_list($ts, $blog = null, $dir = null) {
836    return gmtime(offset_time($ts, $blog, $dir));
837}
838
839function strip_hyphen($s) {
840    return preg_replace('/-+/', '-', $s);
841}
842
843function first_n_words($text, $n) {
844    $text = strip_tags($text);
845    $words = preg_split('/\s+/', $text);
846    $max = count($words) > $n ? $n : count($words);
847    return join(' ', array_slice($words, 0, $max));
848}
849
850function html_text_transform($str = '') {
851    $paras = preg_split('/\r?\n\r?\n/', $str);
852    if ($str == '') {
853        return '';
854    }
855    foreach ($paras as $k => $p) {
856        if (!preg_match('/^<\/?(?:h1|h2|h3|h4|h5|h6|table|ol|dl|ul|menu|dir|p|pre|center|form|select|fieldset|blockquote|address|div|hr)/', $p)) {
857            $p = preg_replace('/\r?\n/', "<br />\n", $p);
858            $p = "<p>$p</p>";
859            $paras[$k] = $p;
860        }
861    }
862    return implode("\n\n", $paras);
863}
864
865function encode_html($str, $quote_style = ENT_COMPAT) {
866    if (!isset($str)) return '';
867    $trans_table = get_html_translation_table(HTML_SPECIALCHARS, $quote_style);
868    if( $trans_table["'"] != '&#039;' ) { # some versions of PHP match single quotes to &#39;
869        $trans_table["'"] = '&#039;';
870    }
871    return (strtr($str, $trans_table));
872}
873
874function decode_html($str, $quote_style = ENT_COMPAT) {
875    if (!isset($str)) return '';
876    $trans_table = get_html_translation_table(HTML_SPECIALCHARS, $quote_style);
877    if( $trans_table["'"] != '&#039;' ) { # some versions of PHP match single quotes to &#39;
878        $trans_table["'"] = '&#039;';
879    }
880    return (strtr($str, array_flip($trans_table)));
881}
882
883function get_category_context(&$ctx, $class = 'category') {
884    # Get our hands on the category for the current context
885    # Either in MTCategories, a Category Archive Template
886    # Or the category for the current entry
887    $cat = $ctx->stash('category') or
888           $ctx->stash('archive_category');
889    if (!isset($cat)) {
890        # No category found so far, test the entry
891        if ($ctx->stash('entry')) {
892            $entry = $ctx->stash('entry');
893            if (empty($entry['placement_category_id']))
894                return null;
895            if ($class == 'folder')
896                $cat = $ctx->mt->db->fetch_folder($entry['placement_category_id']);
897            else
898                $cat = $ctx->mt->db->fetch_category($entry['placement_category_id']);
899 
900            # Return empty string if entry has no category
901            # as the tag has been used in the correct context
902            # but there is no category to work with
903            if (!isset($cat)) {
904                return null;
905            }
906        } else {
907            $tag = $ctx->this_tag();
908            return $ctx->error("$tag must be used in a category context");
909        }
910    }
911    return $cat;
912}
913
914function munge_comment($text, $blog) {
915    if (!$blog['blog_allow_comment_html']) {
916        $text = strip_tags($text);
917    }
918    if ($blog['blog_autolink_urls']) {
919        $text = preg_replace('!(^|\s|>)(https?://[^\s<]+)!s', '$1<a href="$2">$2</a>', $text);
920    }
921    return $text;
922}
923
924function length_text($text) {
925    if (!extension_loaded('mbstring')) {
926        $len = strlen($text);
927    } else {
928        $len = mb_strlen($text);
929    }
930    return $len;
931}
932
933function substr_text($text, $startpos, $length) {
934    if (!extension_loaded('mbstring')) {
935        $text = substr($text, $startpos, $length);
936    } else {
937        $text = mb_substr($text, $startpos, $length);
938    }
939    return $text;
940}
941
942function first_n_text($text, $n) {
943    if (!isset($lang) || empty($lang)) { 
944        global $mt;
945        $lang = ($blog && $blog['blog_language'] ? $blog['blog_language'] : 
946                     $mt->config('DefaultLanguage'));
947    }
948    if ($lang == 'jp') {
949        $lang = 'ja';
950    }
951
952    if ($lang == 'ja') {
953        $text = strip_tags($text);
954        $text = preg_replace('/\r?\n/', " ", $text);
955        return substr_text($text, 0, $n);
956    }else{
957        return first_n_words($text, $n);
958    }
959}
960
961function tag_split_delim($delim, $str) {
962    $delim = quotemeta($delim);
963    $tags = array();
964    $str = trim($str);
965    while (strlen($str) && (preg_match("/^(((['\"])(.*?)\3[^$delim]*?|.*?)($delim\s*|$))/s", $str, $match))) {
966        $str = substr($str, strlen($match[1]));
967        $tag = (isset($match[4]) && $match[4] != '') ? $match[4] : $match[2];
968        $tag = trim($tag);
969        $tag = preg_replace('/\s+/', ' ', $tag);
970        $n8d_tag = tag_normalize($tag);
971        if ($n8d_tag != '')
972            if ($tag != '') $tags[] = $tag;
973    }
974    return $tags;
975}
976
977function tag_split($str) {
978    return tag_split_delim(',', $str);
979}
980
981function catarray_path_length_sort($a, $b) {
982        $al = strlen($a['category_label_path']);
983        $bl = strlen($b['category_label_path']);
984        return $al == $bl ? 0 : $al < $bl ? 1 : -1;
985}
986
987# sorts by length of category label, from longest to shortest
988function catarray_length_sort($a, $b) {
989        $al = strlen($a['category_label']);
990        $bl = strlen($b['category_label']);
991        return $al == $bl ? 0 : $al < $bl ? 1 : -1;
992}
993
994function create_expr_exception($m) {
995    if ($m[2])
996        return '(0)';
997    else
998        return $m[1];
999}
1000
1001function create_cat_expr_function($expr, &$cats, $param) {
1002    global $mt;
1003    $cats_used = array();
1004    $orig_expr = $expr;
1005
1006    $include_children = $param['children'] ? 1 : 0;
1007    $cats_used = array();
1008
1009    if (preg_match('/\//', $expr)) {
1010        foreach ($cats as $id => $cat) {
1011            $catp = category_label_path($cat);
1012            $cats[$id]['category_label_path'] = $catp;
1013        }
1014        $cols = array('category_label_path', 'category_label');
1015    } else {
1016        $cols = array('category_label');
1017    }
1018    foreach ($cols as $col) {
1019        if ($col == 'category_label_path') {
1020            usort($cats, 'catarray_path_length_sort');
1021        } else {
1022            usort($cats, 'catarray_length_sort');
1023        }
1024        $cats_replaced = array();
1025        foreach ($cats as $cat) {
1026            $catl = $cat[$col];
1027            $catid = $cat['category_id'];
1028            $catre = preg_quote($catl, "/");
1029            if (!preg_match("/(?:(?<![#\d])\[$catre\]|$catre)|(?:#$catid\b)/", $expr))
1030                continue;
1031            if ($include_children) {
1032                $kids = array($cat);
1033                $child_cats = array();
1034                while ($c = array_shift($kids)) {
1035                    $child_cats[$c['category_id']] = $c;
1036                    $children = $mt->db->fetch_categories(array('category_id' => $c['category_id'], 'children' => 1, 'show_empty' => 1, 'class' => $c['category_class']));
1037                    if ($children) {
1038                        foreach ($children as $child) {
1039                            $kids[] = $child;
1040                        }
1041                    }
1042                }
1043                $repl = '';
1044                foreach ($child_cats as $ccid => $cc) {
1045                    $repl .= '||#' . $ccid;
1046                }
1047                if (strlen($repl)) $repl = substr($repl, 2);
1048                $repl = '(' . $repl . ')';
1049            } else {
1050                $repl = "(#$catid)";
1051            }
1052            if (isset($cats_replaced[$catl])) {
1053                $last_catid = $cats_replaced[$catl];
1054                $expr = preg_replace("/(#$last_catid\b)/", '($1 || #' . $catid . ')', $expr);
1055            } else {
1056                    $expr = preg_replace("/(?:(?<!#)(?:\[$catre\]|$catre))|#$catid\b/", $repl,
1057                    $expr);
1058                $cats_replaced[$catl] = $catid;
1059            }
1060            if ($include_children) {
1061                foreach ($child_cats as $ccid => $cc)
1062                    $cats_used[$ccid] = $cc;
1063            } else {
1064                $cats_used[$catid] = $cat;
1065            }
1066        }
1067    }
1068
1069    $expr = preg_replace('/\bAND\b/i', '&&', $expr);
1070    $expr = preg_replace('/\bOR\b/i', '||', $expr);
1071    $expr = preg_replace('/\bNOT\b/i', '!', $expr);
1072    $expr = preg_replace_callback('/( |#\d+|&&|\|\||!|\(|\))|([^#0-9&|!()]+)/', 'create_expr_exception', $expr);
1073
1074    # strip out all the 'ok' stuff. if anything is left, we have
1075    # some invalid data in our expression:
1076    $test_expr = preg_replace('/!|&&|\|\||\(0\)|\(|\)|\s|#\d+/', '', $expr);
1077    if ($test_expr != '') {
1078        echo "Invalid category filter: $orig_expr";
1079        return;
1080    }
1081        if (!preg_match('/!/', $expr))
1082            $cats = array_values($cats_used);
1083
1084    $expr = preg_replace('/#(\d+)/', "array_key_exists('\\1', \$pm)", $expr);
1085    $expr = '$pm = array_key_exists($e["entry_id"], $c["p"]) ? $c["p"][$e["entry_id"]] : array(); return (' . $expr . ');';
1086    $fn = create_function('&$e,&$c', $expr);
1087    if ($fn === FALSE) {
1088        echo "Invalid category filter: $orig_expr";
1089        return;
1090    }
1091    return $fn;
1092}
1093
1094function category_label_path($cat) {
1095    global $mt;
1096    $mtdb =& $mt->db;
1097    if (isset($cat['__label_path']))
1098        return $cat['__label_path'];
1099    $result = preg_match('/\//', $cat['category_label']) ? '[' . $cat['category_label'] . ']' : $cat['category_label'];
1100    while ($cat) {
1101        $cat = $cat['category_parent'] ? $mtdb->fetch_category($cat['category_parent']) : null;
1102        if ($cat)
1103            $result = (preg_match('/\//', $cat['category_label']) ? '[' . $cat['category_label'] . ']' : $cat['category_label']) . '/' . $result;
1104    }
1105    # caching this information may be problematic IF
1106    # parent category labels are changed.
1107    $cat['__label_path'] = $result;
1108    return $result;
1109}
1110
1111function cat_path_to_category($path, $blogs = null, $class = 'category') {
1112    global $mt;
1113    if (!$blogs)
1114        $blogs = array('include_blogs' => $mt->blog_id);
1115    if (!is_array($blogs))
1116        $blogs = array($blogs);
1117    $mtdb =& $mt->db;
1118    if (preg_match_all('/(\[[^]]+?\]|[^]\/]+)/', $path, $matches)) {
1119        # split on slashes, fields quoted by []
1120        $cat_path = $matches[1];
1121        for ($i = 0; $i < count($cat_path); $i++) {
1122            $cat_path[$i] = preg_replace('/^\[(.*)\]$/', '\1', $cat_path[$i]);       # remove any []
1123        }
1124        $last_cat_ids = array(0);
1125        foreach ($cat_path as $label) {
1126            $cats = $mtdb->fetch_categories(array_merge($blogs, array('label' => $label, 'parent' => $last_cat_ids, 'show_empty' => 1, 'class' => $class)));
1127            if (!$cats)
1128                break;
1129            $last_cat_ids = array();
1130            foreach ($cats as $cat)
1131                $last_cat_ids[] = $cat['category_id'];
1132        }
1133    }
1134    if ($cats)
1135        return $cats;
1136    if (!$cats && $path) {
1137        $cats = $mtdb->fetch_categories(array_merge($blogs, array('label' => $path, 'show_empty' => 1, 'class' => $class)));
1138        if ($cats)
1139            return $cats;
1140    }
1141    return null;
1142}
1143
1144# sorts by length of tag name, from longest to shortest
1145function tagarray_length_sort($a, $b) {
1146        $al = strlen($a['tag_name']);
1147        $bl = strlen($b['tag_name']);
1148        return $al == $bl ? 0 : $al < $bl ? 1 : -1;
1149}
1150
1151function create_tag_expr_function($expr, &$tags, $datasource = 'entry') {
1152    $tags_used = array();
1153    $orig_expr = $expr;
1154   
1155    # Sort in descending order by length
1156        usort($tags, 'tagarray_length_sort');
1157
1158    # Modify the tag argument, replacing the tag name with '#TagID'
1159    # Create a ID-based hash of the tags that are used in the arg
1160    foreach ($tags as $tag) {
1161        $tagn = $tag['tag_name'];
1162        $tagid = $tag['tag_id'];
1163        $oldexpr = $expr;
1164            $expr = preg_replace("!(?:\Q[$tagn]\E|\Q$tagn\E)!", "#$tagid",
1165                $expr);
1166            if ($oldexpr != $expr)
1167                $tags_used[$tagid] = $tag;
1168        }
1169
1170    # Replace logical constructs with their PHP equivalents
1171    $expr = preg_replace('/\bAND\b/i', '&&', $expr);
1172    $expr = preg_replace('/\bOR\b/i', '||', $expr);
1173    $expr = preg_replace('/\bNOT\b/i', '!', $expr);
1174
1175    # The following is no more readable in PHP than it is in Perl
1176    $expr = preg_replace_callback('/( |#\d+|&&|\|\||!|\(|\))|([^#0-9&|!()]+)/', 'create_expr_exception', $expr);
1177
1178    # Syntax check on 'tag' argument
1179    # Strip out all the valid stuff. if anything is left, we have
1180    # some invalid data in our expression
1181    $test_expr = preg_replace('/!|&&|\|\||\(0\)|\(|\)|\s|#\d+/', '', $expr);
1182    if ($test_expr != '') {
1183        echo "Invalid tag filter: $orig_expr";
1184        return;
1185    }
1186
1187    # Populate array (passed in by reference) of used tags
1188    # but only if expression is positive (i.e. not NOT)
1189        if (!preg_match('/!/', $expr))
1190            $tags = array_values($tags_used);
1191
1192    # Replace '#TagID' with a hash lookup function.
1193    # Function confirms/denies use of tag on entry (by IDs)
1194    $column_name = $datasource . '_id';
1195    $expr = preg_replace('/#(\d+)/', "array_key_exists('\\1', \$tm)", $expr);
1196
1197    # Create a PHP-blessed function of that code and return it
1198    # if all is well.  This function will be used later to
1199    # test for existence of specified tags in entries.
1200    $expr = '$tm = array_key_exists($e["'.$datasource.'_id"], $c["t"]) ? $c["t"][$e["'.$datasource.'_id"]] : array(); return ' . $expr .
1201        ';';
1202    $fn = create_function('&$e,&$c', $expr);
1203    if ($fn === FALSE) {
1204        echo "Invalid tag filter: $orig_expr";
1205        return;
1206    }
1207    return $fn;
1208}
1209
1210function tag_normalize($str) {
1211    # FIXME: character set issues here...
1212    $private = preg_match('/^@/', $str) ? 1 : 0;
1213    $str = preg_replace('/[@!`\\<>\*&#\/~\?\'"\.\,=\(\)\${}\[\];:\ \+\-\r\n]+/', '', $str);
1214    $str = strtolower($str);
1215    if ($private) $str = '@' . $str;
1216    return $str;
1217}
1218
1219function static_path($host) {
1220    global $mt;
1221    $path = $mt->config('StaticWebPath');
1222    if (!$path) {
1223        $path = $mt->config('CGIPath');
1224        if (substr($path, 0, 1) == '/') {  # relative
1225            if (!preg_match('!/$!', $host))
1226                $host .= '/';
1227            if (preg_match('!^(https?://[^/:]+)(:\d+)?/!', $host, $matches)) {
1228                $path = $matches[1] . $path;
1229            }
1230        }
1231        if (substr($path, strlen($path) - 1, 1) != '/')
1232            $path .= '/';
1233        $path .= 'mt-static/';
1234    } elseif (substr($path, 0, 1) == '/') {
1235        if (!preg_match('!/$!', $host))
1236            $host .= '/';
1237        if (preg_match('!^(https?://[^/:]+)(:\d+)?/!', $host, $matches)) {
1238            $path = $matches[1] . $path;
1239        }       
1240    }
1241    if (substr($path, strlen($path) - 1, 1) != '/')
1242        $path .= '/';
1243
1244    return $path;
1245}
1246
1247function static_file_path() {
1248    global $mt;
1249    $path = $mt->config('StaticFilePath');
1250    if (!$path) {
1251        $path = dirname(dirname(dirname(__FILE__)));
1252        $path .= DIRECTORY_SEPARATOR . 'mt-static' . DIRECTORY_SEPARATOR;
1253    }
1254    if (substr($path, strlen($path) - 1, 1) != DIRECTORY_SEPARATOR)
1255        $path .= DIRECTORY_SEPARATOR;
1256
1257    return $path;
1258}
1259
1260function asset_path($path, $blog) {
1261    $site_path = $blog['blog_site_path'];
1262    $site_path = preg_replace('/\/$/', '', $site_path);
1263    $path = preg_replace('/^%r/', $site_path, $path);
1264
1265    $static_file_path = static_file_path();
1266    $static_file_path = preg_replace('/\/$/', '', $static_file_path);
1267    $path = preg_replace('/^%s/', $static_file_path, $path);
1268
1269    $archive_path = $blog['blog_archive_path'];
1270    if ($archive_path) {
1271        $archive_path = preg_replace('/\/$/', '', $archive_path);
1272        $path = preg_replace('/^%a/', $archive_path, $path);
1273    }
1274
1275    return $path;
1276}
1277
1278function userpic_url($asset, $blog, $author) {
1279    global $mt;
1280    $format = $mt->translate('userpic-[_1]-%wx%h%x', array($author['author_id']));
1281    $max_dim = $mt->config('UserpicThumbnailSize');
1282    $ext = '.' . 'png';
1283    $patterns[0] = '/%w/';
1284    $patterns[1] = '/%h/';
1285    $patterns[2] = '/%x/';
1286    $replacement[0] = $max_dim;
1287    $replacement[1] = $max_dim;
1288    $replacement[2] = $ext;
1289    $filename = preg_replace($patterns, $replacement, $format);
1290
1291    # generate thumbnail
1292    $src_file = asset_path($asset['asset_file_path'], $blog);
1293
1294    $cache_path = $mt->config('AssetCacheDir');
1295    $image_path = $cache_path . DIRECTORY_SEPARATOR . 'userpics';
1296    $static_file_path = static_file_path().'support';
1297    make_thumbnail_file($src_file, $static_file_path.DIRECTORY_SEPARATOR.$image_path.DIRECTORY_SEPARATOR.$filename, $max_dim, $max_dim, 0, 'png');
1298
1299    $static_path = $mt->config('StaticWebPath');
1300    $static_path = preg_replace('/\/$/', '', $static_path);
1301    $static_path .= '/support';
1302    $url = sprintf("%s/%s/%s", $static_path, $image_path, $filename);
1303
1304    return $url;
1305}
1306
1307function make_thumbnail_file($src, $dest, $width, $height, $scale = 0, $dest_type = 'auto') {
1308    # Get source image information
1309    list($src_w, $src_h, $src_type, $src_attr) = getimagesize($src);
1310
1311    # Load source image
1312    $src_img;
1313
1314    switch($src_type) {
1315    case 1: #GIF
1316        $src_img = @imagecreatefromgif($src);
1317        break;
1318    case 2: #JPEG
1319        $src_img = @imagecreatefromjpeg($src);
1320        break;
1321    case 3: #PNG
1322        $src_img = @imagecreatefrompng($src);
1323        break;
1324    default: #Unsupported format
1325        return '';
1326    }
1327
1328    if (!$src_img) {
1329        return '';
1330    }
1331
1332    # Calculate thumbnail size
1333    $thumb_w = $src_w;
1334    $thumb_h = $src_h;
1335
1336    if ($scale > 0) {
1337        $thumb_w = $src_w * $scale / 100;
1338        $thumb_h = $src_h * $scale / 100;
1339    } elseif ($width > 0 || $height > 0) {
1340        $x = $width; if ($width > 0) $thumb_w;
1341        $y = $height; if ($height > 0) $thumb_h;
1342        $pct = $width > 0 ? ($x / $thumb_w) : ($y / $thumb_h);
1343        $thumb_w = (int)($thumb_w * $pct);
1344        $thumb_h = (int)($thumb_h * $pct);
1345    }
1346
1347    # Generate
1348    if(!file_exists($dest)) {
1349        $dir_name = dirname($dest);
1350        if (!file_exists($dir_name)) {
1351          mkdir($dir_name, 0777, true);
1352        }
1353        if (!is_writable($dir_name)) {
1354            imagedestroy($src_img);
1355            return '';
1356        }
1357
1358        # Create thumbnail
1359        $dst_img = imagecreatetruecolor ( $thumb_w, $thumb_h );
1360        $result = imagecopyresampled ( $dst_img, $src_img, 0, 0, 0, 0,
1361                    $thumb_w, $thumb_h, $src_w, $src_h);
1362
1363        $output = $src_type;
1364        if ($dest_type != 'auto') {
1365            $output = strtolower($dest_type) == 'gif' ? 1
1366              : strtolower($dest_type) == 'jpeg' ? 2
1367              : strtolower($dest_type) == 'png' ? 3
1368              : $src_type;
1369        }
1370        switch($output) {
1371            case 1: #GIF
1372            imagegif($dst_img, $dest);
1373            break;
1374        case 2: #JPEG
1375            imagejpeg($dst_img, $dest);
1376            break;
1377        case 3: #PNG
1378            imagepng($dst_img, $dest);
1379            break;
1380        }
1381        imagedestroy($dst_img);
1382    }
1383
1384    imagedestroy($src_img);
1385
1386    return array($thumb_w, $thumb_h);
1387}
1388
1389function get_thumbnail_file($asset, $blog, $width = 0, $height = 0, $scale = 0, $format = '%f-thumb-%wx%h%x') {
1390    # Get parameter
1391    $site_path = $blog['blog_site_path'];
1392    $site_path = preg_replace('/\/$/', '', $site_path);
1393    $filename = asset_path($asset['asset_file_path'], $blog);
1394    $name_w = 'auto'; $name_h = 'auto';
1395    if ($width > 0)
1396        $name_w = $width;
1397    if ($height > 0)
1398        $name_h = $height;
1399
1400    # Generate thumbnail file name
1401    $basename = basename($asset['asset_file_name'], '.' . $asset['asset_file_ext']);
1402    $id = $asset['asset_id'];
1403    $ext = '.' . $asset['asset_file_ext'];
1404    $patterns[0] = '/%w/';
1405    $patterns[1] = '/%h/';
1406    $patterns[2] = '/%f/';
1407    $patterns[3] = '/%i/';
1408    $patterns[4] = '/%x/';
1409    $replacement[0] = $name_w;
1410    $replacement[1] = $name_h;
1411    $replacement[2] = $basename;
1412    $replacement[3] = $id;
1413    $replacement[4] = $ext;
1414    $thumb_filename = preg_replace($patterns, $replacement, $format);
1415
1416    # Retrieve thumbnail
1417    global $mt;
1418    $path_parts = pathinfo($filename);
1419    $cache_path = $mt->config('AssetCacheDir');
1420
1421    $ts = preg_replace('![^0-9]!', '', $asset['asset_created_on']);
1422    $date_stamp = format_ts('%Y/%m', $ts, $blog);
1423    $cache_dir = $site_path . DIRECTORY_SEPARATOR . $cache_path . DIRECTORY_SEPARATOR . $date_stamp . DIRECTORY_SEPARATOR;
1424    $thumb_name = $cache_dir . $thumb_filename;
1425 
1426    # generate thumbnail
1427    list ($thumb_w, $thumb_h) = make_thumbnail_file($filename, $thumb_name, $width, $height);
1428
1429    # make url
1430    $basename = basename($thumb_name);
1431    $thumb_url = $blog['blog_site_url'] . $cache_path . '/' . $date_stamp . '/' . $basename;
1432
1433    return array($thumb_url, $thumb_w, $thumb_h, $thumb_name);
1434}
1435
1436function asset_cleanup($str) {
1437    $str = preg_replace_callback('/<(?:[Ff][Oo][Rr][Mm]|[Ss][Pp][Aa][Nn])([^>]*?)\smt:asset-id="\d+"([^>]+?>)(.*?)<\/(?:[Ff][Oo][Rr][Mm]|[Ss][Pp][Aa][Nn])>/s', 'asset_cleanup_cb', $str);
1438    return $str;
1439}
1440
1441function asset_cleanup_cb($matches) {
1442    $attr = $matches[1] . $matches[2];
1443    $inner = $matches[3];
1444    $attr = preg_replace('/\s[Cc][Oo][Nn][Tt][Ee][Nn][Tt][Ee][Dd][Ii][Tt][Aa][Bb][Ll][Ee]=([\'"][^\'"]*?[\'"]|[Ff][Aa][Ll][Ss][Ee])/', '', $attr);
1445    return '<span' . $attr . $inner . '</span>';
1446}
1447
1448function create_role_expr_function($expr, &$roles, $datasource = 'author') {
1449    $roles_used = array();
1450    $orig_expr = $expr;
1451   
1452    foreach ($roles as $role) {
1453        $rolen = $role['role_name'];
1454        $roleid = $role['role_id'];
1455        $oldexpr = $expr;
1456        $expr = preg_replace("!(?:\Q[$rolen]\E|\Q$rolen\E)!", "#$roleid", $expr);
1457        if ($oldexpr != $expr)
1458            $roles_used[$roleid] = $role;
1459    }
1460
1461    $expr = preg_replace('/\bOR\b/i', '||', $expr);
1462    $expr = preg_replace_callback('/( |#\d+|&&|\|\||!|\(|\))|([^#0-9&|!()]+)/', 'create_expr_exception', $expr);
1463
1464    $test_expr = preg_replace('/!|&&|\|\||\(0\)|\(|\)|\s|#\d+/', '', $expr);
1465    if ($test_expr != '') {
1466        echo "Invalid role filter: $orig_expr";
1467        return;
1468    }
1469
1470    if (!preg_match('/!/', $expr))
1471        $roles = array_values($roles_used);
1472
1473    $column_name = $datasource . '_id';
1474    $expr = preg_replace('/#(\d+)/', "array_key_exists('\\1', \$tm)", $expr);
1475
1476    $expr = '$tm = array_key_exists($e["'.$datasource.'_id"], $c["r"]) ? $c["r"][$e["'.$datasource.'_id"]] : array(); return ' . $expr . ';';
1477    $fn = create_function('&$e,&$c', $expr);
1478    if ($fn === FALSE) {
1479        echo "Invalid role filter: $orig_expr";
1480        return;
1481    }
1482    return $fn;
1483}
1484
1485function create_status_expr_function($expr, &$status, $datasource = 'author') {
1486    $orig_expr = $expr;
1487
1488    foreach ($status as $s) {
1489        $statusn = $s['name'];
1490        $statusid = $s['id'];
1491        $oldexpr = $expr;
1492        $expr = preg_replace("!(?:\Q[$statusn]\E|\Q$statusn\E)!", "#$statusid", $expr);
1493    }
1494
1495    $expr = preg_replace('/\bOR\b/i', '||', $expr);
1496    $expr = preg_replace_callback('/( |#\d+|&&|\|\||!|\(|\))|([^#0-9&|!()]+)/', 'create_expr_exception', $expr);
1497
1498    $test_expr = preg_replace('/!|&&|\|\||\(0\)|\(|\)|\s|#\d+/', '', $expr);
1499    if ($test_expr != '') {
1500        echo "Invalid status filter: $orig_expr";
1501        return;
1502    }
1503
1504    $expr = preg_replace('/#(\d+)/', '$e["'.$datasource.'_status"] == \\1', $expr);
1505    $expr = 'return ' . $expr . ';';
1506
1507    $fn = create_function('&$e,&$c', $expr);
1508    if ($fn === FALSE) {
1509        echo "Invalid status filter: $orig_expr";
1510        return;
1511    }
1512    return $fn;
1513}
1514
1515function create_rating_expr_function($expr, $filter, $namespace, $datasource = 'entry') {
1516    $orig_expr = $expr;
1517
1518    require_once 'rating_lib.php';
1519    $expr = '$ctx = $c; if ($ctx == null) { global $mt; $ctx = $mt->context(); } $old = $ctx->mt->db->result; ';
1520    if ($filter == 'min_score') {
1521        $expr .= '$ret = score_for($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  >= '.$orig_expr.';';
1522    } elseif ($filter == 'max_score') {
1523        $expr .= '$ret = score_for($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  <= '.$orig_expr.';';
1524    } elseif ($filter == 'min_rate') {
1525        $expr .= '$ret = score_avg($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  >= '.$orig_expr.';';
1526    } elseif ($filter == 'max_rate') {
1527        $expr .= '$ret = score_avg($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  <= '.$orig_expr.';';
1528    } elseif ($filter == 'min_count') {
1529        $expr .= '$ret = score_count($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  >= '.$orig_expr.';';
1530    } elseif ($filter == 'max_count') {
1531        $expr .= '$ret = score_count($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'")  <= '.$orig_expr.';';
1532    } elseif ($filter == 'scored_by') {
1533        $expr .= '$ret = get_score($ctx, $e["'.$datasource.'_id"], "'.$datasource.'", "'.$namespace.'", '.$orig_expr.') > 0;';
1534    }
1535    $expr .= ' $ctx->mt->db->result = $old; return $ret;';
1536
1537    $fn = create_function('&$e,&$c', $expr);
1538    if ($fn === FALSE) {
1539        echo "Invalid rating filter: $orig_expr";
1540        return;
1541    }
1542    return $fn;
1543}
1544
1545function _math_operation($op, $lvalue, $rvalue) {
1546    if (!preg_match('/^\-?[\d\.]+$/', $lvalue))
1547        return;
1548    if ( isset($rvalue) && !preg_match('/^\-?[\d\.]+$/', $rvalue))
1549        return;
1550    if ( !isset($rvalue)
1551      && $op != 'inc' && $op != 'dec' && $op != '++' && $op != '--' )
1552        return;
1553    if ( ( '+' == $op ) || ( 'add' == $op ) ) {
1554        return $lvalue + $rvalue;
1555    }
1556    elseif ( ( '++' == $op ) || ( 'inc' == $op ) ) {
1557        return $lvalue + 1;
1558    }
1559    elseif ( ( '-' == $op ) || ( 'sub' == $op ) ) {
1560        return $lvalue - $rvalue;
1561    }
1562    elseif ( ( '--' == $op ) || ( 'dec' == $op ) ) {
1563        return $lvalue - 1;
1564    }
1565    elseif ( ( '*' == $op ) || ( 'mul' == $op ) ) {
1566        return $lvalue * $rvalue;
1567    }
1568    elseif ( ( '/' == $op ) || ( 'div' == $op ) ) {
1569        if ( $rvalue == 0 )
1570            return;
1571        return $lvalue / $rvalue;
1572    }
1573    elseif ( ( '%' == $op ) || ( 'mod' == $op ) ) {
1574        // to be in line with perl equivalent
1575        $lvalue = floor($lvalue);
1576        $rvalue = floor($rvalue);
1577        if ( $rvalue == 0 )
1578            return;
1579        return $lvalue % $rvalue;
1580    }
1581    return;
1582}
1583
1584?>
Note: See TracBrowser for help on using the browser.