source: trunk/forum/includes/bbcode.php

Last change on this file was 702, checked in by george, 15 years ago
  • Upraveno: Aktualizace fóra.
File size: 16.1 KB
Line 
1<?php
2/**
3*
4* @package phpBB3
5* @version $Id$
6* @copyright (c) 2005 phpBB Group
7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8*
9*/
10
11/**
12* @ignore
13*/
14if (!defined('IN_PHPBB'))
15{
16 exit;
17}
18
19/**
20* BBCode class
21* @package phpBB3
22*/
23class bbcode
24{
25 var $bbcode_uid = '';
26 var $bbcode_bitfield = '';
27 var $bbcode_cache = array();
28 var $bbcode_template = array();
29
30 var $bbcodes = array();
31
32 var $template_bitfield;
33 var $template_filename = '';
34
35 /**
36 * Constructor
37 * Init bbcode cache entries if bitfield is specified
38 */
39 function bbcode($bitfield = '')
40 {
41 if ($bitfield)
42 {
43 $this->bbcode_bitfield = $bitfield;
44 $this->bbcode_cache_init();
45 }
46 }
47
48 /**
49 * Second pass bbcodes
50 */
51 function bbcode_second_pass(&$message, $bbcode_uid = '', $bbcode_bitfield = false)
52 {
53 if ($bbcode_uid)
54 {
55 $this->bbcode_uid = $bbcode_uid;
56 }
57
58 if ($bbcode_bitfield !== false)
59 {
60 $this->bbcode_bitfield = $bbcode_bitfield;
61
62 // Init those added with a new bbcode_bitfield (already stored codes will not get parsed again)
63 $this->bbcode_cache_init();
64 }
65
66 if (!$this->bbcode_bitfield)
67 {
68 // Remove the uid from tags that have not been transformed into HTML
69 if ($this->bbcode_uid)
70 {
71 $message = str_replace(':' . $this->bbcode_uid, '', $message);
72 }
73
74 return;
75 }
76
77 $str = array('search' => array(), 'replace' => array());
78 $preg = array('search' => array(), 'replace' => array());
79
80 $bitfield = new bitfield($this->bbcode_bitfield);
81 $bbcodes_set = $bitfield->get_all_set();
82
83 $undid_bbcode_specialchars = false;
84 foreach ($bbcodes_set as $bbcode_id)
85 {
86 if (!empty($this->bbcode_cache[$bbcode_id]))
87 {
88 foreach ($this->bbcode_cache[$bbcode_id] as $type => $array)
89 {
90 foreach ($array as $search => $replace)
91 {
92 ${$type}['search'][] = str_replace('$uid', $this->bbcode_uid, $search);
93 ${$type}['replace'][] = $replace;
94 }
95
96 if (sizeof($str['search']))
97 {
98 $message = str_replace($str['search'], $str['replace'], $message);
99 $str = array('search' => array(), 'replace' => array());
100 }
101
102 if (sizeof($preg['search']))
103 {
104 // we need to turn the entities back into their original form to allow the
105 // search patterns to work properly
106 if (!$undid_bbcode_specialchars)
107 {
108 $message = str_replace(array('&#58;', '&#46;'), array(':', '.'), $message);
109 $undid_bbcode_specialchars = true;
110 }
111
112 $message = preg_replace($preg['search'], $preg['replace'], $message);
113 $preg = array('search' => array(), 'replace' => array());
114 }
115 }
116 }
117 }
118
119 // Remove the uid from tags that have not been transformed into HTML
120 $message = str_replace(':' . $this->bbcode_uid, '', $message);
121 }
122
123 /**
124 * Init bbcode cache
125 *
126 * requires: $this->bbcode_bitfield
127 * sets: $this->bbcode_cache with bbcode templates needed for bbcode_bitfield
128 */
129 function bbcode_cache_init()
130 {
131 global $phpbb_root_path, $template, $user;
132
133 if (empty($this->template_filename))
134 {
135 $this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']);
136 $this->template_filename = $phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template/bbcode.html';
137
138 if (!@file_exists($this->template_filename))
139 {
140 if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'])
141 {
142 $this->template_filename = $phpbb_root_path . 'styles/' . $user->theme['template_inherit_path'] . '/template/bbcode.html';
143 if (!@file_exists($this->template_filename))
144 {
145 trigger_error('The file ' . $this->template_filename . ' is missing.', E_USER_ERROR);
146 }
147 }
148 else
149 {
150 trigger_error('The file ' . $this->template_filename . ' is missing.', E_USER_ERROR);
151 }
152 }
153 }
154
155 $bbcode_ids = $rowset = $sql = array();
156
157 $bitfield = new bitfield($this->bbcode_bitfield);
158 $bbcodes_set = $bitfield->get_all_set();
159
160 foreach ($bbcodes_set as $bbcode_id)
161 {
162 if (isset($this->bbcode_cache[$bbcode_id]))
163 {
164 // do not try to re-cache it if it's already in
165 continue;
166 }
167 $bbcode_ids[] = $bbcode_id;
168
169 if ($bbcode_id > NUM_CORE_BBCODES)
170 {
171 $sql[] = $bbcode_id;
172 }
173 }
174
175 if (sizeof($sql))
176 {
177 global $db;
178
179 $sql = 'SELECT *
180 FROM ' . BBCODES_TABLE . '
181 WHERE ' . $db->sql_in_set('bbcode_id', $sql);
182 $result = $db->sql_query($sql, 3600);
183
184 while ($row = $db->sql_fetchrow($result))
185 {
186 // To circumvent replacing newlines with <br /> for the generated html,
187 // we use carriage returns here. They are later changed back to newlines
188 $row['bbcode_tpl'] = str_replace("\n", "\r", $row['bbcode_tpl']);
189 $row['second_pass_replace'] = str_replace("\n", "\r", $row['second_pass_replace']);
190
191 $rowset[$row['bbcode_id']] = $row;
192 }
193 $db->sql_freeresult($result);
194 }
195
196 foreach ($bbcode_ids as $bbcode_id)
197 {
198 switch ($bbcode_id)
199 {
200 case 0:
201 $this->bbcode_cache[$bbcode_id] = array(
202 'str' => array(
203 '[/quote:$uid]' => $this->bbcode_tpl('quote_close', $bbcode_id)
204 ),
205 'preg' => array(
206 '#\[quote(?:=&quot;(.*?)&quot;)?:$uid\]((?!\[quote(?:=&quot;.*?&quot;)?:$uid\]).)?#ise' => "\$this->bbcode_second_pass_quote('\$1', '\$2')"
207 )
208 );
209 break;
210
211 case 1:
212 $this->bbcode_cache[$bbcode_id] = array(
213 'str' => array(
214 '[b:$uid]' => $this->bbcode_tpl('b_open', $bbcode_id),
215 '[/b:$uid]' => $this->bbcode_tpl('b_close', $bbcode_id),
216 )
217 );
218 break;
219
220 case 2:
221 $this->bbcode_cache[$bbcode_id] = array(
222 'str' => array(
223 '[i:$uid]' => $this->bbcode_tpl('i_open', $bbcode_id),
224 '[/i:$uid]' => $this->bbcode_tpl('i_close', $bbcode_id),
225 )
226 );
227 break;
228
229 case 3:
230 $this->bbcode_cache[$bbcode_id] = array(
231 'preg' => array(
232 '#\[url:$uid\]((.*?))\[/url:$uid\]#s' => $this->bbcode_tpl('url', $bbcode_id),
233 '#\[url=([^\[]+?):$uid\](.*?)\[/url:$uid\]#s' => $this->bbcode_tpl('url', $bbcode_id),
234 )
235 );
236 break;
237
238 case 4:
239 if ($user->optionget('viewimg'))
240 {
241 $this->bbcode_cache[$bbcode_id] = array(
242 'preg' => array(
243 '#\[img:$uid\](.*?)\[/img:$uid\]#s' => $this->bbcode_tpl('img', $bbcode_id),
244 )
245 );
246 }
247 else
248 {
249 $this->bbcode_cache[$bbcode_id] = array(
250 'preg' => array(
251 '#\[img:$uid\](.*?)\[/img:$uid\]#s' => str_replace('$2', '[ img ]', $this->bbcode_tpl('url', $bbcode_id, true)),
252 )
253 );
254 }
255 break;
256
257 case 5:
258 $this->bbcode_cache[$bbcode_id] = array(
259 'preg' => array(
260 '#\[size=([\-\+]?\d+):$uid\](.*?)\[/size:$uid\]#s' => $this->bbcode_tpl('size', $bbcode_id),
261 )
262 );
263 break;
264
265 case 6:
266 $this->bbcode_cache[$bbcode_id] = array(
267 'preg' => array(
268 '!\[color=(#[0-9a-f]{3}|#[0-9a-f]{6}|[a-z\-]+):$uid\](.*?)\[/color:$uid\]!is' => $this->bbcode_tpl('color', $bbcode_id),
269 )
270 );
271 break;
272
273 case 7:
274 $this->bbcode_cache[$bbcode_id] = array(
275 'str' => array(
276 '[u:$uid]' => $this->bbcode_tpl('u_open', $bbcode_id),
277 '[/u:$uid]' => $this->bbcode_tpl('u_close', $bbcode_id),
278 )
279 );
280 break;
281
282 case 8:
283 $this->bbcode_cache[$bbcode_id] = array(
284 'preg' => array(
285 '#\[code(?:=([a-z]+))?:$uid\](.*?)\[/code:$uid\]#ise' => "\$this->bbcode_second_pass_code('\$1', '\$2')",
286 )
287 );
288 break;
289
290 case 9:
291 $this->bbcode_cache[$bbcode_id] = array(
292 'preg' => array(
293 '#(\[\/?(list|\*):[mou]?:?$uid\])[\n]{1}#' => "\$1",
294 '#(\[list=([^\[]+):$uid\])[\n]{1}#' => "\$1",
295 '#\[list=([^\[]+):$uid\]#e' => "\$this->bbcode_list('\$1')",
296 ),
297 'str' => array(
298 '[list:$uid]' => $this->bbcode_tpl('ulist_open_default', $bbcode_id),
299 '[/list:u:$uid]' => $this->bbcode_tpl('ulist_close', $bbcode_id),
300 '[/list:o:$uid]' => $this->bbcode_tpl('olist_close', $bbcode_id),
301 '[*:$uid]' => $this->bbcode_tpl('listitem', $bbcode_id),
302 '[/*:$uid]' => $this->bbcode_tpl('listitem_close', $bbcode_id),
303 '[/*:m:$uid]' => $this->bbcode_tpl('listitem_close', $bbcode_id)
304 ),
305 );
306 break;
307
308 case 10:
309 $this->bbcode_cache[$bbcode_id] = array(
310 'preg' => array(
311 '#\[email:$uid\]((.*?))\[/email:$uid\]#is' => $this->bbcode_tpl('email', $bbcode_id),
312 '#\[email=([^\[]+):$uid\](.*?)\[/email:$uid\]#is' => $this->bbcode_tpl('email', $bbcode_id)
313 )
314 );
315 break;
316
317 case 11:
318 if ($user->optionget('viewflash'))
319 {
320 $this->bbcode_cache[$bbcode_id] = array(
321 'preg' => array(
322 '#\[flash=([0-9]+),([0-9]+):$uid\](.*?)\[/flash:$uid\]#' => $this->bbcode_tpl('flash', $bbcode_id),
323 )
324 );
325 }
326 else
327 {
328 $this->bbcode_cache[$bbcode_id] = array(
329 'preg' => array(
330 '#\[flash=([0-9]+),([0-9]+):$uid\](.*?)\[/flash:$uid\]#' => str_replace('$1', '$3', str_replace('$2', '[ flash ]', $this->bbcode_tpl('url', $bbcode_id, true)))
331 )
332 );
333 }
334 break;
335
336 case 12:
337 $this->bbcode_cache[$bbcode_id] = array(
338 'str' => array(
339 '[/attachment:$uid]' => $this->bbcode_tpl('inline_attachment_close', $bbcode_id)
340 ),
341 'preg' => array(
342 '#\[attachment=([0-9]+):$uid\]#' => $this->bbcode_tpl('inline_attachment_open', $bbcode_id)
343 )
344 );
345 break;
346
347 default:
348 if (isset($rowset[$bbcode_id]))
349 {
350 if ($this->template_bitfield->get($bbcode_id))
351 {
352 // The bbcode requires a custom template to be loaded
353 if (!$bbcode_tpl = $this->bbcode_tpl($rowset[$bbcode_id]['bbcode_tag'], $bbcode_id))
354 {
355 // For some reason, the required template seems not to be available, use the default template
356 $bbcode_tpl = (!empty($rowset[$bbcode_id]['second_pass_replace'])) ? $rowset[$bbcode_id]['second_pass_replace'] : $rowset[$bbcode_id]['bbcode_tpl'];
357 }
358 else
359 {
360 // In order to use templates with custom bbcodes we need
361 // to replace all {VARS} to corresponding backreferences
362 // Note that backreferences are numbered from bbcode_match
363 if (preg_match_all('/\{(URL|LOCAL_URL|EMAIL|TEXT|SIMPLETEXT|INTTEXT|IDENTIFIER|COLOR|NUMBER)[0-9]*\}/', $rowset[$bbcode_id]['bbcode_match'], $m))
364 {
365 foreach ($m[0] as $i => $tok)
366 {
367 $bbcode_tpl = str_replace($tok, '$' . ($i + 1), $bbcode_tpl);
368 }
369 }
370 }
371 }
372 else
373 {
374 // Default template
375 $bbcode_tpl = (!empty($rowset[$bbcode_id]['second_pass_replace'])) ? $rowset[$bbcode_id]['second_pass_replace'] : $rowset[$bbcode_id]['bbcode_tpl'];
376 }
377
378 // Replace {L_*} lang strings
379 $bbcode_tpl = preg_replace('/{L_([A-Z_]+)}/e', "(!empty(\$user->lang['\$1'])) ? \$user->lang['\$1'] : ucwords(strtolower(str_replace('_', ' ', '\$1')))", $bbcode_tpl);
380
381 if (!empty($rowset[$bbcode_id]['second_pass_replace']))
382 {
383 // The custom BBCode requires second-pass pattern replacements
384 $this->bbcode_cache[$bbcode_id] = array(
385 'preg' => array($rowset[$bbcode_id]['second_pass_match'] => $bbcode_tpl)
386 );
387 }
388 else
389 {
390 $this->bbcode_cache[$bbcode_id] = array(
391 'str' => array($rowset[$bbcode_id]['second_pass_match'] => $bbcode_tpl)
392 );
393 }
394 }
395 else
396 {
397 $this->bbcode_cache[$bbcode_id] = false;
398 }
399 break;
400 }
401 }
402 }
403
404 /**
405 * Return bbcode template
406 */
407 function bbcode_tpl($tpl_name, $bbcode_id = -1, $skip_bitfield_check = false)
408 {
409 static $bbcode_hardtpl = array();
410 if (empty($bbcode_hardtpl))
411 {
412 global $user;
413
414 $bbcode_hardtpl = array(
415 'b_open' => '<span style="font-weight: bold">',
416 'b_close' => '</span>',
417 'i_open' => '<span style="font-style: italic">',
418 'i_close' => '</span>',
419 'u_open' => '<span style="text-decoration: underline">',
420 'u_close' => '</span>',
421 'img' => '<img src="$1" alt="' . $user->lang['IMAGE'] . '" />',
422 'size' => '<span style="font-size: $1%; line-height: normal">$2</span>',
423 'color' => '<span style="color: $1">$2</span>',
424 'email' => '<a href="mailto:$1">$2</a>'
425 );
426 }
427
428 if ($bbcode_id != -1 && !$skip_bitfield_check && !$this->template_bitfield->get($bbcode_id))
429 {
430 return (isset($bbcode_hardtpl[$tpl_name])) ? $bbcode_hardtpl[$tpl_name] : false;
431 }
432
433 if (empty($this->bbcode_template))
434 {
435 if (($tpl = file_get_contents($this->template_filename)) === false)
436 {
437 trigger_error('Could not load bbcode template', E_USER_ERROR);
438 }
439
440 // replace \ with \\ and then ' with \'.
441 $tpl = str_replace('\\', '\\\\', $tpl);
442 $tpl = str_replace("'", "\'", $tpl);
443
444 // strip newlines and indent
445 $tpl = preg_replace("/\n[\n\r\s\t]*/", '', $tpl);
446
447 // Turn template blocks into PHP assignment statements for the values of $bbcode_tpl..
448 $this->bbcode_template = array();
449
450 $matches = preg_match_all('#<!-- BEGIN (.*?) -->(.*?)<!-- END (?:.*?) -->#', $tpl, $match);
451
452 for ($i = 0; $i < $matches; $i++)
453 {
454 if (empty($match[1][$i]))
455 {
456 continue;
457 }
458
459 $this->bbcode_template[$match[1][$i]] = $this->bbcode_tpl_replace($match[1][$i], $match[2][$i]);
460 }
461 }
462
463 return (isset($this->bbcode_template[$tpl_name])) ? $this->bbcode_template[$tpl_name] : ((isset($bbcode_hardtpl[$tpl_name])) ? $bbcode_hardtpl[$tpl_name] : false);
464 }
465
466 /**
467 * Return bbcode template replacement
468 */
469 function bbcode_tpl_replace($tpl_name, $tpl)
470 {
471 global $user;
472
473 static $replacements = array(
474 'quote_username_open' => array('{USERNAME}' => '$1'),
475 'color' => array('{COLOR}' => '$1', '{TEXT}' => '$2'),
476 'size' => array('{SIZE}' => '$1', '{TEXT}' => '$2'),
477 'img' => array('{URL}' => '$1'),
478 'flash' => array('{WIDTH}' => '$1', '{HEIGHT}' => '$2', '{URL}' => '$3'),
479 'url' => array('{URL}' => '$1', '{DESCRIPTION}' => '$2'),
480 'email' => array('{EMAIL}' => '$1', '{DESCRIPTION}' => '$2')
481 );
482
483 $tpl = preg_replace('/{L_([A-Z_]+)}/e', "(!empty(\$user->lang['\$1'])) ? \$user->lang['\$1'] : ucwords(strtolower(str_replace('_', ' ', '\$1')))", $tpl);
484
485 if (!empty($replacements[$tpl_name]))
486 {
487 $tpl = strtr($tpl, $replacements[$tpl_name]);
488 }
489
490 return trim($tpl);
491 }
492
493 /**
494 * Second parse list bbcode
495 */
496 function bbcode_list($type)
497 {
498 if ($type == '')
499 {
500 $tpl = 'ulist_open_default';
501 $type = 'default';
502 }
503 else if ($type == 'i')
504 {
505 $tpl = 'olist_open';
506 $type = 'lower-roman';
507 }
508 else if ($type == 'I')
509 {
510 $tpl = 'olist_open';
511 $type = 'upper-roman';
512 }
513 else if (preg_match('#^(disc|circle|square)$#i', $type))
514 {
515 $tpl = 'ulist_open';
516 $type = strtolower($type);
517 }
518 else if (preg_match('#^[a-z]$#', $type))
519 {
520 $tpl = 'olist_open';
521 $type = 'lower-alpha';
522 }
523 else if (preg_match('#[A-Z]#', $type))
524 {
525 $tpl = 'olist_open';
526 $type = 'upper-alpha';
527 }
528 else if (is_numeric($type))
529 {
530 $tpl = 'olist_open';
531 $type = 'decimal';
532 }
533 else
534 {
535 $tpl = 'olist_open';
536 $type = 'decimal';
537 }
538
539 return str_replace('{LIST_TYPE}', $type, $this->bbcode_tpl($tpl));
540 }
541
542 /**
543 * Second parse quote tag
544 */
545 function bbcode_second_pass_quote($username, $quote)
546 {
547 // when using the /e modifier, preg_replace slashes double-quotes but does not
548 // seem to slash anything else
549 $quote = str_replace('\"', '"', $quote);
550 $username = str_replace('\"', '"', $username);
551
552 // remove newline at the beginning
553 if ($quote == "\n")
554 {
555 $quote = '';
556 }
557
558 $quote = (($username) ? str_replace('$1', $username, $this->bbcode_tpl('quote_username_open')) : $this->bbcode_tpl('quote_open')) . $quote;
559
560 return $quote;
561 }
562
563 /**
564 * Second parse code tag
565 */
566 function bbcode_second_pass_code($type, $code)
567 {
568 // when using the /e modifier, preg_replace slashes double-quotes but does not
569 // seem to slash anything else
570 $code = str_replace('\"', '"', $code);
571
572 switch ($type)
573 {
574 case 'php':
575 // Not the english way, but valid because of hardcoded syntax highlighting
576 if (strpos($code, '<span class="syntaxdefault"><br /></span>') === 0)
577 {
578 $code = substr($code, 41);
579 }
580
581 // no break;
582
583 default:
584 $code = str_replace("\t", '&nbsp; &nbsp;', $code);
585 $code = str_replace(' ', '&nbsp; ', $code);
586 $code = str_replace(' ', ' &nbsp;', $code);
587
588 // remove newline at the beginning
589 if (!empty($code) && $code[0] == "\n")
590 {
591 $code = substr($code, 1);
592 }
593 break;
594 }
595
596 $code = $this->bbcode_tpl('code_open') . $code . $this->bbcode_tpl('code_close');
597
598 return $code;
599 }
600}
601
602?>
Note: See TracBrowser for help on using the repository browser.