phpBB
Statistics
| Revision:

root / trunk / phpBB / includes / bbcode.php

History | View | Annotate | Download (16.1 kB)

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