/[drupal]/contributions/modules/bbcode/bbcode-filter.inc
ViewVC logotype

Contents of /contributions/modules/bbcode/bbcode-filter.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.66 - (show annotations) (download) (as text)
Sun Nov 30 08:50:08 2008 UTC (11 months, 3 weeks ago) by naudefj
Branch: MAIN
CVS Tags: DRUPAL-6--1-2, HEAD
Changes since 1.65: +2 -2 lines
File MIME type: text/x-php
#280851 by executex: Allow - and _ in bbcode tag
1 <?php
2 // $Id: bbcode-filter.inc,v 1.65 2008/01/27 16:50:12 naudefj Exp $
3
4 function _bbcode_filter_process(&$body, $format = -1) {
5
6 $quote_text = t('Quote:');
7 $quote_user = t('\\1 wrote:');
8
9 // Encode all script tags to prevent XSS html injection attacks
10 $body = preg_replace(array('#<script([^>]*)>#i', '#</script([^>]*)>#i'), array('&lt;script\\1&gt;', '&lt;/script\\1&gt;'), $body);
11
12 // Find all [code] tags and check if they contain a newline. If we find a newline,
13 // that [code] should be rendered as a block, otherwise it will still be inline
14 $mode = variable_get("bbcode_paragraph_breaks_$format", 2);
15 $pre = array(); $i = 0;
16 if (preg_match_all('#\[code(?::\w+)?\](.*?)\[/code(?::\w+)?\]#si', $body, $code_tags, PREG_SET_ORDER)) {
17 foreach ($code_tags as $code_tag) {
18 $code_tag[1] = str_replace(array('<', '>'), array('&lt;', '&gt;'), $code_tag[1]);
19 if (strpos($code_tag[1], "\n") === FALSE)
20 $body = str_replace($code_tag[0], '<code class="bb-code">'. $code_tag[1] .'</code>', $body);
21 elseif ($mode) {
22 // Strip preformatted code blocks from text during line break processing, replaced below
23 $body = str_replace($code_tag[0], "***pRe_sTrInG$i***", $body);
24 $pre[$i++] = '<pre class="bb-code-block">'. $code_tag[1] .'</pre>';
25 }
26 else
27 $body = str_replace($code_tag[0], '<pre class="bb-code-block">'. $code_tag[1] .'</pre>', $body);
28 }
29 }
30
31 // Apply line and paragraph breaks (skipping preformatted code)
32 if ($mode) {
33
34 if ($mode == 1) // Line breaks only (starting with PHP 4.0.5, nl2br() is XHTML compliant)
35 $body = nl2br($body);
36
37 if ($mode == 2) { // Line and paragraph breaks (may not always be XHTML compliant)
38 $body = preg_replace("/(\r\n|\n|\r)/", "\n", $body);
39 $body = preg_replace("/\n\n+/", "\n\n", $body);
40 $parts = explode("\n\n", $body);
41 for ($i=0; $i<sizeof($parts); $i++) {
42 // No linebreaks if paragraph starts with an HTML tag
43 if ( !preg_match('/^<.*>/', $parts[$i]) )
44 $parts[$i] = nl2br($parts[$i]);
45
46 // Some tags should not be in paragraph blocks
47 if ( !preg_match('/^(?:<|\[)(?:table|list|ol|ul|pre|select|form|blockquote|hr)/i', $parts[$i]) )
48 $parts[$i] = '<p>'. $parts[$i] .'</p>';
49 }
50 $body = implode("\n\n", $parts);
51 }
52
53 // Reinsert preformatted code blocks
54 foreach ($pre as $i => $code_tag)
55 $body = str_replace("***pRe_sTrInG$i***", $code_tag, $body);
56 }
57
58 // Add closing tags to prevent users from disruping your site's HTML
59 // (required for nestable tags only: [list] and [quote])
60 preg_match_all('/\[quote/i', $body, $matches);
61 $opentags = count($matches['0']);
62 preg_match_all('/\[\/quote\]/i', $body, $matches);
63 $unclosed = $opentags - count($matches['0']);
64 for ($i = 0; $i < $unclosed; $i++)
65 $body .= '[/quote]';
66 preg_match_all('/\[list/i', $body, $matches);
67 $opentags = count($matches['0']);
68 preg_match_all('/\[\/list\]/i', $body, $matches);
69 $unclosed = $opentags - count($matches['0']);
70 for ($i = 0; $i < $unclosed; $i++)
71 $body .= '[/list]';
72
73 // begin processing for [size]
74 if (stristr($body, '[size=') !== FALSE) { // prevent useless processing
75 $arr = array(
76 'tag' => 'size',
77 'pattern' => '#\[\x07=([\d]+)(?::\w+)?\]([^\x07]*)\[/\x07(?::\w+)?\]#esi',
78 'replacement' => '"<span style=\"font-size:". _bbcode_round_size_val(\'$1\') ."px\">". str_replace(\'\"\', \'"\', \'$2\') ."</span>"',
79 'text' => $body);
80 $body = _bbcode_replace_nest_tag($arr);
81 } // end processing for [size]
82
83 // begin processing for [color]
84 if (stristr($body, '[color=') !== FALSE) { // prevent useless processing
85 $arr = array(
86 'tag' => 'color',
87 'pattern' => '#\[\x07=([\#\w]+)(?::\w+)?\]([^\x07]*)\[/\x07(?::\w+)?\]#si',
88 'replacement' => '<span style="color:$1">$2</span>',
89 'text' => $body);
90 $body = _bbcode_replace_nest_tag($arr);
91 } // end processing for [color]
92
93 // begin processing for [font]
94 if (stristr($body, '[font=') !== FALSE) { // prevent useless processing
95 $arr = array(
96 'tag' => 'font',
97 'pattern' => '#\[\x07=([\w\s]+)(?::\w+)?\]([^\x07]*)\[/\x07(?::\w+)?\]#si',
98 'replacement' => '<span style="font-family:$1">$2</span>',
99 'text' => $body);
100 $body = _bbcode_replace_nest_tag($arr);
101 } // end processing for [font]
102
103 // begin processing for [list] and [*]
104 if (stristr($body, '[list') !== FALSE) { // prevent useless processing
105 $l_type = array(
106 NULL => array('style' => 'circle', 'tag' => 'ul'),
107 'c' => array('style' => 'circle', 'tag' => 'ul'),
108 'd' => array('style' => 'disc', 'tag' => 'ul'),
109 's' => array('style' => 'square', 'tag' => 'ul'),
110 '1' => array('style' => 'decimal', 'tag' => 'ol'),
111 'a' => array('style' => 'lower-alpha', 'tag' => 'ol'),
112 'A' => array('style' => 'upper-alpha', 'tag' => 'ol'),
113 'i' => array('style' => 'lower-roman', 'tag' => 'ol'),
114 'I' => array('style' => 'upper-roman', 'tag' => 'ol')
115 );
116 $body = preg_replace('#(\[[/]*)list(.*?\])#si', "$1\x07$2", $body);
117
118 // replace to <li> tags - [*]..[*]|[*]..[/list]
119 $body = preg_replace('#\[\*(?::\w+)?\]([^\x07]*?)(?=\s*?(\[\*(?::\w+)?\]|\[/\x07(?::\w+)?\]))#si', '<li>$1</li>', $body);
120 // add </li> tags to nested <li> - [/list]..[/list]
121 $body = preg_replace('#(\[/\x07(?::\w+)?\])(?=[^\x07]*?\[/\x07(?::\w+)?\])#si', '$1</li>', $body);
122 // add </li> tags to nested <li> - [/list]..[*]..[list]
123 $body = preg_replace('#(\[/\x07(?::\w+)?\])(?=[^\x07]*?\[\*(?::\w+)?\][^\x07]*?\[\x07.*(?::\w+)?\])#si', '$1</li>', $body);
124 // replace to <li> tags for nested <li> - [*]..[list]
125 $body = preg_replace('#\[\*(?::\w+)?\]([^\x07]*)?(?=\[\x07.*(?::\w+)?\])#si', '<li>$1', $body);
126
127 // replace to <ol>/<ul> and </ol>/</ul> tags
128 // It will be better to use &count and do-while, if php 5 or higher.
129 while (preg_match("#\[\x07[=]*((?-i)[cds1aAiI])*(?::\w+)?\]([^\x07]*)\[/\x07(?::\w+)?\]#si", $body)) {
130 $body = preg_replace("#\[\x07[=]*((?-i)[cds1aAiI])*(?::\w+)?\]([^\x07]*)\[/\x07(?::\w+)?\]#esi", '"<". $l_type[\'$1\']["tag"] ." class=\"bb-list\" style=\"list-style-type:". $l_type[\'$1\']["style"] .";\">". str_replace(\'\"\', \'"\', \'$2\') ."</". $l_type[\'$1\']["tag"] .">"', $body);
131 }
132
133 // remove <br /> tags
134 $body = preg_replace('#(<[/]*([uo]l|li).*>.*)<br />#i', '$1', $body);
135 } // end processing for [list] and [*]
136
137 // Define BBCode tags
138 $preg = array(
139 // Implement [notag]
140 '#\[notag(?::\w+)?\](.*?)\[/notag(?::\w+)?\]#sie' => '_bbcode_notag_tag(\'\\1\')',
141
142 // Headings and indexes - articles will almost always need them
143 '#\[h([1-6])(?::\w+)?\](.*?)\[/h[1-6](?::\w+)?\]#sie' => '_bbcode_generate_heading(\\1, \'\\2\')',
144 '#\[index\s*/?\]#sie' => '_bbcode_generate_index($body)',
145 '#\[index style=(ol|ul)\]#sie' => '_bbcode_generate_index($body, \'\\1\')',
146
147 // Font, text and alignment
148 '#\[align=(\w+)(?::\w+)?\](.*?)\[/align(?::\w+)?\]#si' => '<span style="text-align:\\1">\\2</span>',
149 '#\[float=(left|right)(?::\w+)?\](.*?)\[/float(?::\w+)?\]#si' => '<span style="float:\\1">\\2</span>',
150 '#\[justify(?::\w+)?\](.*?)\[/justify(?::\w+)?\]#si' => '<div style="text-align:justify;">\\1</div>',
151 '#\[(b|strong)(?::\w+)?\](.*?)\[/(b|strong)(?::\w+)?\]#si' => '<span style="font-weight:bold">\\2</span>',
152 '#\[(i|em)(?::\w+)?\](.*?)\[/(i|em)(?::\w+)?\]#si' => '<span style="font-style:italic">\\2</span>',
153 '#\[u(?::\w+)?\](.*?)\[/u(?::\w+)?\]#si' => '<span style="text-decoration:underline">\\1</span>',
154 '#\[s(?::\w+)?\](.*?)\[/s(?::\w+)?\]#si' => '<s>\\1</s>',
155 '#\[sup(?::\w+)?\](.*?)\[/sup(?::\w+)?\]#si' => '<sup>\\1</sup>',
156 '#\[sub(?::\w+)?\](.*?)\[/sub(?::\w+)?\]#si' => '<sub>\\1</sub>',
157 '#\[center(?::\w+)?\](.*?)\[/center(?::\w+)?\]#si' => '<div style="text-align:center">\\1</div>',
158 '#\[left(?::\w+)?\](.*?)\[/left(?::\w+)?\]#si' => '<div style="text-align:left">\\1</div>',
159 '#\[right(?::\w+)?\](.*?)\[/right(?::\w+)?\]#si' => '<div style="text-align:right">\\1</div>',
160
161 // Links without a protocol, with a protocol, and with good looking text
162 '#\[url(?::\w+)?\]www\.([\w:;&,%+~!=@\/\.\-\#\?]+?)\[/url(?::\w+)?\]#si' => '<a href="http://www.\\1" class="bb-url">\\1</a>',
163 '#\[url(?::\w+)?\]([\w:;&,%+~!=@\/\.\-\#\?]+?)\[/url(?::\w+)?\]#si' => '<a href="\\1" class="bb-url">\\1</a>',
164 '#\[url=www\.([\w:;&,%+~!=@\/\.\-\#\?]+?)\](.*?)\[/url(?::\w+)?\]#si' => '<a href="http://www.\\1" class="bb-url">\\2</a>',
165 '#\[url=([\w:;&,%+~!=@\/\.\-\#\?]+?)\](.*?)\[/url(?::\w+)?\]#si' => '<a href="\\1" class="bb-url">\\2</a>',
166
167 // Anchor tags for linking within documents
168 '#\[anchor=(\w+)(?::\w+)?\](.*?)\[/anchor(?::\w+)?\]#si' => '<a name="\\1">\\2</a>',
169
170 // Images without or with client-side sizing
171 '#\[img(?::\w+)?\]([\w:;&,~%+!=@\/\.\-\#\?]+)\[/img(?::\w+)?\]#si' => '<img src="\\1" alt="" class="bb-image" />',
172 '#\[img=(\d+)x(\d+)(?::\w+)?\]([\w:;&,~%+!=@\/\.\-\#\?]+)\[/img(?::\w+)?\]#si' => '<img width="\\1" height="\\2" alt="" src="\\3" class="bb-image" />',
173 '#\[img=([\w\s:;,\.\-\'\(\)]+)(?::\w+)?\]([\w:;&,~%+!=@\/\.\-\#\?]+)\[/img(?::\w+)?\]#si' => '<img alt="\\1" src="\\2" class="bb-image" />',
174 '#\[img align=(left|right|center)(?::\w+)?\]([\w:;&,~%+!=@\/\.\-\#\?]+)\[/img(?::\w+)?\]#si' => '<img src="\\2" alt="" align="\\1" class="bb-image" />',
175
176 // Flash animations and other special effects
177 '#\[flash=(\d+)x(\d+)(?::\w+)?\]([\w:;&,~%+!=@\/\.\-\#\?]+)\[/flash(?::\w+)?\]#si' => '<object type="application/x-shockwave-flash" data="\\3" width="\\1" height="\\2"><param name="movie" value="\\3" /></object>',
178
179 // Acronyms & abbreviations - show description when mouse moves over tag
180 '#\[acronym=([\w\s-,\.]+)(?::\w+)?\](.*?)\[/acronym(?::\w+)?\]#si' => '<acronym title="\\1">\\2</acronym>',
181 '#\[abbr=([\w\s-,\.]+)(?::\w+)?\](.*?)\[/abbr(?::\w+)?\]#si' => '<abbr title="\\1">\\2</abbr>',
182
183 // Quoting with or without specifying the source
184 '#\[quote(?::\w+)?\]#i' => '<div class="bb-quote">'.$quote_text.'<blockquote class="bb-quote-body">',
185 '#\[quote=(?:&quot;|"|\')?(.*?)["\']?(?:&quot;|"|\')?\]#i' => '<div class="bb-quote"><b>'.$quote_user.'</b><blockquote class="bb-quote-body">',
186 '#\[/quote(?::\w+)?\]#si' => '</blockquote></div>',
187
188 // PHP code blocks (syntax highlighted)
189 '#\[php(?::\w+)?\](?:[\r\n])*(.*?)\[/php(?::\w+)?\]#sie' => '_bbcode_php_tag(\'\\1\')',
190
191 // Links to popular sites
192 '#\[google(?::\w+)?\]([\w\s-]+?)\[/google(?::\w+)?\]#si' => '<a href="http://www.google.com/search?q=\\1">\\1</a>',
193 '#\[wikipedia(?::\w+)?\]([\w\s-]+?)\[/wikipedia(?::\w+)?\]#si' => '<a href="http://www.wikipedia.org/wiki/\\1">\\1</a>',
194 '#\[youtube\]([0-9a-zA-Z_\-]+)\[/youtube\]#si' => '<object width="425" height="366"><param name="movie" value="http://www.youtube.com/v/\\1"></param><embed src="http://www.youtube.com/v/\\1" type="application/x-shockwave-flash" width="425" height="366"></embed></object>',
195
196 // Table tags
197 '#\[table\](.+?)\[/table\]#si' => '<table class="bb-table">\\1</table>',
198 '#\[(row|r|tr)\](.+?)\[/(row|r|tr)\]#si' => '<tr>\\2</tr>',
199 '#\[(row|r|tr) color=([\#\w]+)\](.+?)\[/(row|r|tr)\]#si' => '<tr bgcolor=\\2>\\3</tr>',
200 '#\[(header|head|h)\](.+?)\[/(header|head|h)\]#si' => '<th>\\2</th>',
201 '#\[(col|c|td)\](.+?)\[/(col|c|td)\]#si' => '<td valign="top">\\2</td>',
202
203 // Cleanup table output (td, th and tr tags)
204 '#<([\/]?)t([dhr])><br />#si' => '<\\1t\\2>',
205 '#<table(.+?)><br />#si' => '<table\\1>',
206 );
207 $body = preg_replace(array_keys($preg), array_values($preg), $body);
208
209 // Simple replacements (str_replace is faster than preg_replace)
210 $str = array(
211 // Horizontal delimiter
212 '[hr]' => '<hr class="bb-hr" />',
213 // Force line break
214 '[br]' => '<br class="bb-br" />',
215 // Force space
216 '[sp]' => '&nbsp;',
217 );
218 $body = str_replace(array_keys($str), array_values($str), $body);
219
220 // We cannot evaluate the variable in callback function because
221 // there is no way to pass the $format variable
222 if (variable_get("bbcode_encode_mailto_$format", 1)) {
223 // Replacing email addresses with encoded html
224 $body = preg_replace_callback('#\[email(?::\w+)?\]([\w\.\-\+~@]+)\[/email(?::\w+)?\]#si', '_bbcode_encode_mailto', $body);
225 $body = preg_replace_callback('#\[email=(.*?)(?::\w+)?\](.*?)\[/email(?::\w+)?\]#si', '_bbcode_encode_mailto', $body);
226 }
227 else {
228 $body = preg_replace(
229 array('#\[email(?::\w+)?\](.*?)\[/email(?::\w+)?\]#si','#\[email=(.*?)(?::\w+)?\]([\w\s]+)\[/email(?::\w+)?\]#si'),
230 array('<a href="mailto:\\1" class="bb-email">\\1</a>', '<a href="mailto:\\1" class="bb-email">\\2</a>'),
231 $body);
232 }
233
234 // Turns web and e-mail addresses into clickable links
235 if (variable_get("bbcode_make_links_$format", 1)) {
236
237 // pad with a space so we can match things at the start of the 1st line
238 $ret = ' ' . $body;
239 // padding to already filtered links
240 $ret = preg_replace('#(<a.+>)(.+</a>)#i', "$1\x07$2", $ret);
241
242 // matches an "xxx://yyyy" URL at the start of a line, or after a space.
243 // xxxx can only be alpha characters.
244 // yyyy is anything up to the first space, newline, comma, double quote or <
245 $ret = preg_replace('#(?<=^|[\t\r\n >\(\[\]\|])([a-z]+?://[\w\-]+\.([\w\-]+\.)*\w+(:[0-9]+)?(/[^ "\'\(\n\r\t<\)\[\]\|]*)?)((?<![,\.])|(?!\s))#i', '<a href="\1">\1</a>', $ret);
246
247 // matches a "www|ftp.xxxx.yyyy[/zzzz]" kinda lazy URL thing
248 // Must contain at least 2 dots. xxxx contains either alphanum, or "-"
249 // zzzz is optional.. will contain everything up to the first space, newline,
250 // comma, double quote or <.
251 $ret = preg_replace('#([\t\r\n >\(\[\|])(www|ftp)\.(([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^ \"\'\(\n\r\t<\)\[\]\|]*)?)#i', '\1<a href="http://\2.\3">\2.\3</a>', $ret);
252
253 // matches an email@domain type address at the start of a line, or after a space.
254 // Note: Only the followed chars are valid; alphanums, "-", "_" and or ".".
255 if (variable_get("bbcode_encode_mailto_$format", 1))
256 $ret = preg_replace_callback("#([\t\r\n ])([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i", '_bbcode_encode_mailto', $ret);
257 else
258 $ret = preg_replace('#([\t\r\n ])([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i', '\\1<a href="mailto:\\2@\\3">\\2@\\3</a>', $ret);
259
260 // Remove our padding
261 $ret = str_replace("\x07", '', $ret);
262 $body = substr($ret, 1);
263 }
264
265 if (variable_get("bbcode_filter_nofollow_$format", 0)) {
266 $body = preg_replace('#<a([^>]+)>#i', '<a\\1 rel="nofollow">', $body);
267 }
268
269 return $body;
270 }
271
272 function _bbcode_generate_heading($level, $text) {
273 $anchor = preg_replace('/([\s]+)/', '_', $text);
274 $anchor = preg_replace('/([\W]+)/', '', $anchor);
275 return '<h'. $level .'><a name="'. $anchor .'">'. $text .'</a></h'. $level .'>';
276 }
277
278 function _bbcode_generate_index($body, $tag = 'ol') {
279 $level = 0;
280 $index = '<'. $tag .">\n";
281 $close_tags = 0;
282
283 if (preg_match_all('#\[h([1-6]).*?\](.*?)\[/h([1-6]).*?\]#si', $body, $head_tags, PREG_SET_ORDER)) {
284 foreach ($head_tags as $head_tag) {
285 if ($level == 0) $level = $head_tag[1];
286 $anchor = preg_replace('/([\s]+)/', '_', $head_tag[2]);
287 $anchor = preg_replace('/([\W]+)/', '', $anchor);
288
289 if ($head_tag[1] > $level) {
290 $index .= '<'. $tag .">\n";
291 $index .= '<li><a href="#'. $anchor .'">'. $head_tag[2] ."</a>\n";
292 $close_tags++;
293 $level = $head_tag[1];
294 } else if ($head_tag[1] < $level) {
295 while ($close_tags > 0) {
296 $index .= '</'. $tag .">\n";
297 $close_tags--;
298 }
299 $index .= '<li><a href="#'. $anchor .'">'. $head_tag[2] ."</a>\n";
300 $level = $head_tag[1];
301 } else {
302 $index .= '<li><a href="#'. $anchor .'">'. $head_tag[2] ."</a>\n";
303 $level = $head_tag[1];
304 }
305 }
306 }
307 while ($close_tags >= 0) {
308 $index .= '</'. $tag .">\n";
309 $close_tags--;
310 }
311 return $index;
312 }
313
314 function _bbcode_encode_mailto($matches) {
315 if (isset($matches[3]))
316 $link = 'document.write(\'<a href="mailto:' . $matches[2].'@'.$matches[3] . '">' . $matches[2].'@'.$matches[3] . '</a>\');';
317 else
318 $link = 'document.write(\'<a href="mailto:' . $matches[1] . '" class="bb-email">' . (isset($matches[2]) ? $matches[2] : $matches[1]) . '</a>\');';
319
320 $js_encode = '';
321 for ($x = 0; $x < strlen($link); $x++)
322 $js_encode .= '%' . bin2hex($link{$x});
323
324 $link = '<script type="text/javascript">eval(unescape(\''.$js_encode.'\'))</script>';
325 if (isset($matches[3]))
326 $link = $matches[1] . $link;
327
328 return $link;
329 }
330
331 function _bbcode_notag_tag($text = NULL) {
332 return str_replace( array('[', ']', '@'), array('&#91;', '&#93;', '&#64;'), stripslashes($text));
333 }
334
335 function _bbcode_php_tag($text = NULL) {
336 return '<pre>'. highlight_string( str_replace('<br />', '', stripslashes($text)), true) .'</pre>';
337 }
338
339 function _bbcode_round_size_val($size) {
340 if ($size < 6)
341 return 6;
342 elseif ($size > 48)
343 return 48;
344 else
345 return $size;
346 }
347
348 function _bbcode_replace_nest_tag($arr = NULL) {
349 $text = preg_replace('#(\[[/]*)'. $arr['tag'] .'(.*?\])#si', "$1\x07$2", $arr['text']);
350 // It will be better to use &count and do-while, if php 5 or higher.
351 while (preg_match($arr['pattern'], $text)) {
352 $text = preg_replace($arr['pattern'], $arr['replacement'], $text);
353 }
354 return $text;
355 }
356

  ViewVC Help
Powered by ViewVC 1.1.2