Parent directory
acceder au document
<?php
/***********************************************************************

Copyright (C) 2002-2005 Rickard Andersson (rickard@punbb.org)

This file was part of PunBB.

PunBB is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

PunBB is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA

************************************************************************
editor Pierre-Emmanuel Périllon
*/

if (is_dir('inclure') )
{
require_once('inclure/SourceCode.class.php');
}
else
{
require_once('../inclure/SourceCode.class.php');
}

DEFINE('BBCODE_CSSCLASS_GRAS', 'bold' );
DEFINE('BBCODE_CSSCLASS_ITALIQUE', 'italic');
DEFINE('BBCODE_CSSCLASS_SOUSLIGNE', 'underline');
DEFINE('BBCODE_CSSCLASS_INCQBOX', 'incqbox');
DEFINE('BBCODE_CSSCLASS_CODEBOX', 'codebox');
DEFINE('BBCODE_CSSCLASS_SCROLLBOX', 'scrollbox');
DEFINE('BBCODE_CSSCLASS_SIGIMAG', 'sigimag');
DEFINE('BBCODE_CSSCLASS_POSTIMG', 'postimg');



class BBCode
{
// Here you can add additional smilies if you like (please note that you must escape singlequote and backslash)

var $smiley_text = array(
':)', '=)', ':|',
'=|', ':(', '=(',
':D', '=D', ':o',
':O', ';)', ':/',
':P', ':lol:', ':mad:',
':rolleyes:', ':cool:');
var $smiley_img = array(
'smile.png', 'smile.png', 'neutral.png',
'neutral.png', 'sad.png', 'sad.png',
'big_smile.png', 'big_smile.png', 'yikes.png',
'yikes.png', 'wink.png', 'hmm.png',
'tongue.png', 'lol.png', 'mad.png',
'roll.png', 'cool.png');

var $text = '';
//see check_tag_order
var $error = FALSE;
var $errors ;
var $overflow = FALSE;
var $o_indent_num_spaces = 8;
var $show_smilies = TRUE;
var $show_img_sig = TRUE;
var $show_img = TRUE;
var $o_make_links = TRUE;
var $p_message_bbcode = TRUE;
var $p_message_img_tag = TRUE;
var $smiliesDirectory = '../images/smilies/';

var $lang_common= array(
'BBCode error' => 'Une erreur est survenue',
'BBCode error 1' => 'code 1: trop de citations imbriquées',
'BBCode error 2' => 'code 2: balise fermante [/code] manquante',
'BBCode error 3' => 'code 3: balise fermante [/code] en trop',
'BBCode error 4' => 'code 4: pas assez de balise [/quote]',
'BBCode error 5' => 'code 5: trop de balise [/quote]',
'Image link' => 'Cliquez pour voir l\'image',
'Code' => 'Code',
'namedQuote' =>'écrit par ',
'anonymousQuote' => 'Citation');


/**
* constructeur
*
*/
function BBCode( $convert_link = TRUE, $convert_smilies = TRUE)
{
$this->show_smilies = $convert_smilies;
$this->o_make_links = $convert_link;
}

// Uncomment the next row if you add smilies that contain any of the characters &"'<>
//$smiley_text = array_map('pun_htmlspecialchars', $smiley_text);


/*
* Make sure all BBCodes are lower case and do a little cleanup
*/
function _preparse_bbcode($is_signature = false)
{
// Change all simple BBCodes to lower case
$a = array('[B]', '[I]', '[U]', '[/B]', '[/I]', '[/U]');
$b = array('[b]', '[i]', '[u]', '[/b]', '[/i]', '[/u]');
$this->text = str_replace($a, $b, $this->text);

// Do the more complex BBCodes (also strip excessive whitespace and useless quotes)
$a = array(
'#\[url=("|\'|)(.*?)\\1\]\s*#i',
'#\[url\]\s*#i',
'#\s*\[/url\]#i',
'#\[email=("|\'|)(.*?)\\1\]\s*#i',
'#\[email\]\s*#i',
'#\s*\[/email\]#i',
'#\[img\]\s*(.*?)\s*\[/img\]#is',
'#\[colou?r=("|\'|)(.*?)\\1\](.*?)\[/colou?r\]#is');

$b = array(
'[url=$2]',
'[url]',
'[/url]',
'[email=$2]',
'[email]',
'[/email]',
'[img]$1[/img]',
'[color=$2]$3[/color]');

if (!$is_signature)
{
// For non-signatures, we have to do the quote and code tags as well
$a[] = '#\[quote=(&quot;|"|\'|)(.*?)\\1\]\s*#i';
$a[] = '#\[quote\]\s*#i';
$a[] = '#\s*\[/quote\]\s*#i';
$a[] = '#\[code\][\r\n]*(.*?)\s*\[/code\]\s*#is';

$b[] = '[quote=$1$2$1]';
$b[] = '[quote]';
$b[] = '[/quote]'."\n";
$b[] = '[code]$1[/code]'."\n";
}

// Run this baby!
$this->text = preg_replace($a, $b, $this->text);

if (!$is_signature)
{
$this->overflow = $this->_check_tag_order($this->text);

if ($this->error)
{
// A BBCode error was spotted in check_tag_order()
$this->errors[] = $this->error;
}
else if ($this->overflow)
{
// The quote depth level was too high, so we strip out the inner most quote(s)
$this->text = substr($this->text, 0, $overflow[0]).substr($this->text, $overflow[1], (strlen($this->text) - $overflow[0]));
}
// else
// {
// echo '<p>quote et code active mais pas d\'erreur</p>';
// }
// }
// else
// {
// //TODO
// echo '<p>quote et code désactivé</p>';
// }
// dire que pas de quote dans notre
// else
// {
// global $lang_prof_reg;

// if (preg_match('#\[quote=(&quot;|"|\'|)(.*)\\1\]|\[quote\]|\[/quote\]|\[code\]|\[/code\]#i', $this->text))
// message($lang_prof_reg['Signature quote/code']);
}

$this->text = trim($this->text);
}


/**
* Parse text and make sure that [code] and [quote] syntax is correct
* attention ne pas faire un passage par référence. c'est bien une copie de
* la chaine à donner.
*/
function _check_tag_order( $text )
{
// The maximum allowed quote depth
$max_depth = 3;

$cur_index = 0;
$q_depth = 0;

while (true)
{
// Look for regular code and quote tags
$c_start = strpos($text, '[code]');
$c_end = strpos($text, '[/code]');
$q_start = strpos($text, '[quote]');
$q_end = strpos($text, '[/quote]');

// Look for [quote=username] style quote tags
if (preg_match('#\[quote=(&quot;|"|\'|)(.*)\\1\]#sU', $text, $matches))
{
$q2_start = strpos($text, $matches[0]);
}
else
{
$q2_start = 65536;
}

// Deal with strpos() returning false when the string is not found
// (65536 is one byte longer than the maximum post length)
if ($c_start === false) $c_start = 65536;
if ($c_end === false) $c_end = 65536;
if ($q_start === false) $q_start = 65536;
if ($q_end === false) $q_end = 65536;

// If none of the strings were found
if (min($c_start, $c_end, $q_start, $q_end, $q2_start) == 65536)
{
break;
}

// We are interested in the first quote (regardless of the type of quote)
$q3_start = ($q_start < $q2_start) ? $q_start : $q2_start;

// We found a [quote] or a [quote=username]
if ($q3_start < min($q_end, $c_start, $c_end))
{
$step = ($q_start < $q2_start) ? 7 : strlen($matches[0]);

$cur_index += $q3_start + $step;

// Did we reach $max_depth?
if ($q_depth == $max_depth)
$overflow_begin = $cur_index - $step;

++$q_depth;
$text = substr($text, $q3_start + $step);
}

// We found a [/quote]
else if ($q_end < min($q_start, $c_start, $c_end))
{
if ($q_depth == 0)
{
$this->error=$this->lang_common['BBCode error'].' '.$this->lang_common['BBCode error 1'];
return;
}

$q_depth--;
$cur_index += $q_end+8;

// Did we reach $max_depth?
if ($q_depth == $max_depth)
$overflow_end = $cur_index;

$text = substr($text, $q_end+8);
}

// We found a [code]
else if ($c_start < min($c_end, $q_start, $q_end))
{
// Make sure there's a [/code] and that any new [code] doesn't occur before the end tag
$tmp = strpos($text, '[/code]');
$tmp2 = strpos(substr($text, $c_start+6), '[code]');
if ($tmp2 !== false)
$tmp2 += $c_start+6;

if ($tmp === false || ($tmp2 !== false && $tmp2 < $tmp))
{
$this->error = $this->lang_common['BBCode error'].' '.$this->lang_common['BBCode error 2'];
return;
}
else
$text = substr($text, $tmp+7);

$cur_index += $tmp+7;
}

// We found a [/code] (this shouldn't happen since we handle both start and end tag in the if clause above)
else if ($c_end < min($c_start, $q_start, $q_end))
{
$this->error = $this->lang_common['BBCode error'].' '.$this->lang_common['BBCode error 3'];
return;
}
}

// If $q_depth <> 0 something is wrong with the quote syntax
if ($q_depth)
{
$this->error = $this->lang_common['BBCode error'].' '.$this->lang_common['BBCode error 4'];
return;
}
else if ($q_depth < 0)
{
$this->error = $this->lang_common['BBCode error'].' '.$this->lang_common['BBCode error 5'];
return;
}

// If the quote depth level was higher than $max_depth we return the index for the
// beginning and end of the part we should strip out
if (isset($overflow_begin))
{
return array($overflow_begin, $overflow_end);
}
else
{
return null;
}
}


/**
* Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
*/
function _split_text($text, $start, $end)
{

$tokens = explode($start, $text);

$outside[] = $tokens[0];

$num_tokens = count($tokens);
for ($i = 1; $i < $num_tokens; ++$i)
{
$temp = explode($end, $tokens[$i]);
$inside[] = $temp[0];
$outside[] = $temp[1];
}

if ($this->o_indent_num_spaces != 8 && $start == '[code]')
{
$spaces = str_repeat(' ', $this->o_indent_num_spaces);
$inside = str_replace("\t", $spaces, $inside);
}

return array($inside, $outside);
}


/**
* @static
* Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
*/
function handle_url_tag($url, $link = '')
{

$full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
if (strpos($url, 'www.') === 0)
{
// If it starts with www, we add http://
$full_url = 'http://'.$full_url;
}
else if (strpos($url, 'ftp.') === 0)
{
// Else if it starts with ftp, we add ftp://
$full_url = 'ftp://'.$full_url;
}
else if (!preg_match('#^([a-z0-9]{3,6})://#', $url, $bah))
{
// Else if it doesn't start with abcdef://, we add http://
$full_url = 'http://'.$full_url;
}
// Ok, not very pretty :-)
$link = ($link == '' || $link == $url) ? ((strlen($url) > 55) ? substr($url, 0 , 39).' &hellip; '.substr($url, -10) : $url) : stripslashes($link);

return '<a href="'.$full_url.'">'.$link.'</a>';
}


/**
* Turns an URL from the [img] tag into an <img> tag or a <a href...> tag
*/
function _handle_img_tag($url, $is_signature = false)
{

$img_tag = '<a href="'.$url.'">&lt;'.$this->lang_common['Image link'].'&gt;</a>';

if ($is_signature && $this->show_img_sig != '0')
{
$img_tag = '<img class="'.BBCODE_CSSCLASS_SIGIMAG.'" src="'.$url.'" alt="'.htmlspecialchars($url).'" />';
}
else if (!$is_signature && $this->show_img )
{
$img_tag = '<img class="'.BBCODE_CSSCLASS_POSTIMG.'" src="'.$url.'" alt="'.htmlspecialchars($url).'" />';
}
return $img_tag;
}


/**
* Convert BBCodes to their HTML equivalent
*/
function _do_bbcode(& $text)
{

if (strpos($text, 'quote') !== false)
{
// $text = str_replace('[quote]', '</p><blockquote><div class="incqbox"><p>', $text);
// $text = preg_replace('#\[quote=(&quot;|"|\'|)(.*)\\1\]#seU', '"</p><blockquote><div class=\"incqbox\"><h4>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\')." ".$lang_common[\'wrote\'].":</h4><p>"', $text);
// $text = preg_replace('#\[\/quote\]\s*#', '</p></div></blockquote><p>', $text);



$text = str_replace(
'[quote]',
'</p><blockquote><div class="'.BBCODE_CSSCLASS_INCQBOX.'"><h4>'.$this->lang_common['anonymousQuote'].':</h4><p>',
$text);
$text = preg_replace(
'#\[quote=(&quot;|"|\'|)(.*)\\1\]#seU',
'"</p><blockquote><div class=\"'.BBCODE_CSSCLASS_INCQBOX.'\"><h4>".$this->lang_common[\'namedQuote\']." ".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), \'$2\').":</h4><p>"',
$text);
$text = preg_replace(
'#\[\/quote\]\s*#',
'</p></div></blockquote><p>',
$text);
}

$pattern = array(
'#\[b\](.*?)\[/b\]#s',
'#\[i\](.*?)\[/i\]#s',
'#\[u\](.*?)\[/u\]#s',
'#\[url\]([^\[<]*?)\[/url\]#e',
'#\[url=([^\[<]*?)\](.*?)\[/url\]#e',
'#\[email\]([^\[<]*?)\[/email\]#',
'#\[email=([^\[<]*?)\](.*?)\[/email\]#',
'#\[color=([a-zA-Z]*|\#?[0-9a-fA-F]{6})](.*?)\[/color\]#s');

$replace = array(
'<span class="'.BBCODE_CSSCLASS_GRAS.'">$1</span>',
'<span class="'.BBCODE_CSSCLASS_ITALIQUE.'">$1</span>',
'<span class="'.BBCODE_CSSCLASS_SOUSLIGNE.'">$1</span>',
'BBCode::handle_url_tag(\'$1\')',
'BBCode::handle_url_tag(\'$1\', \'$2\')',
'<a href="mailto:$1">$1</a>',
'<a href="mailto:$1">$2</a>',
'<span style="color: $1">$2</span>');

// This thing takes a while! :)
$text = preg_replace($pattern, $replace, $text);

return $text;
}


/**
* Make hyperlinks clickable
*/
function do_clickable(&$text)
{

$text = ' '.$text;

$text = preg_replace(
'#([\s\(\)])(https?|ftp|news){1}://([\w\-]+\.([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^"\s\(\)<\[]*)?)#ie',
'\'$1\'.BBCode::handle_url_tag(\'$2://$3\')',
$this->text);
$text = preg_replace(
'#([\s\(\)])(www|ftp)\.(([\w\-]+\.)*[\w]+(:[0-9]+)?(/[^"\s\(\)<\[]*)?)#ie',
'\'$1\'.BBCode::handle_url_tag(\'$2.$3\', \'$2.$3\')',
$text);

$text = substr($text, 1);
return $text;
}


/**
* Convert a series of smilies to images
*/
function _do_smilies(& $text)
{

$text = ' '.$text.' ';

$num_smilies = count($this->smiley_text);
for ($i = 0; $i < $num_smilies; ++$i)
{
$a = "#(?<=.\W|\W.|^\W)".preg_quote($this->smiley_text[$i], '#')."(?=.\W|\W.|\W$)#m";
$b = '$1<img src="'.$this->smiliesDirectory.$this->smiley_img[$i].'" width="15" height="15" alt="'.substr($this->smiley_img[$i],0,strrpos($this->smiley_img[$i], '.')).'" />$2';
$this->text = preg_replace($a, $b, $text);
}
return substr($text, 1, -1);
}


/**
* @static
* Equivalent to htmlspecialchars(), but allows &#[0-9]+ (for unicode)
*/
function pun_htmlspecialchars(& $str)
{
$str = preg_replace('/&(?!#[0-9]+;)/s', '&amp;', $str);
$str = str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $str);

return $str;
}


/**
* Parse message text
*/
function _parse_message($hide_smilies = FALSE)
{

// if ($pun_config['o_censoring'] == '1')
// $this->text = censor_words($this->text);

// Convert applicable characters to HTML entities
$this->text = BBCode::pun_htmlspecialchars($this->text);

// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
if (strpos($this->text, '[code]') !== false && strpos($this->text, '[/code]') !== false)
{
list($inside, $outside) = $this->_split_text($this->text, '[code]', '[/code]');
$outside = array_map('ltrim', $outside);
$this->text = implode('<">', $outside);
}

if ($this->o_make_links)
{
$this->text = BBCode::do_clickable($this->text);
}

if ( $this->show_smilies && !$hide_smilies )
{
$this->text = $this->_do_smilies($this->text);
}

if ($this->p_message_bbcode && strpos($this->text, '[') !== false && strpos($this->text, ']') !== false)
{
$this->text= $this->_do_bbcode($this->text);

if ($this->p_message_img_tag)
{
// $this->text = preg_replace('#\[img\]((ht|f)tps?://)([^\s<"]*?)\.(jpg|jpeg|png|gif)\[/img\]#e', '$this->handle_img_tag(\'$1$3.$4\')', $this->text);
$this->text = preg_replace(
'#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e',
'$this->_handle_img_tag(\'$1$3\')',
$this->text);
}
}

// Deal with newlines, tabs and multiple spaces
$pattern = array("\n", "\t", ' ', ' ');
$replace = array('<br />', '&nbsp; &nbsp; ', '&nbsp; ', ' &nbsp;');
$this->text = str_replace($pattern, $replace, $this->text);

// If we split up the message before we have to concatenate it together again (code tags)
if (isset($inside))
{
$outside = explode('<">', $this->text);
$this->text = '';

$num_tokens = count($outside);

for ($i = 0; $i < $num_tokens; ++$i)
{
$this->text .= $outside[$i];
if (isset($inside[$i]))
{
$num_lines = ((substr_count($inside[$i], "\n")) + 3) * 1.5;
$height_str = ($num_lines > 35) ? '35em' : $num_lines.'em';
$this->text .= '</p><div class="'.BBCODE_CSSCLASS_CODEBOX.'"><div class="'.BBCODE_CSSCLASS_INCQBOX.'"><h4>'.$this->lang_common['Code'].':</h4><div class="'.BBCODE_CSSCLASS_SCROLLBOX.'" style="height: '.$height_str.'"><pre>'.$inside[$i].'</pre></div></div></div><p>';
}
}
}

// Add paragraph tag around post, but make sure there are no empty paragraphs
$this->text = str_replace('<p></p>', '', '<p>'.$this->text.'</p>');

}

/**
* la fonction MAGIQUE à appeler
*/
function parse(& $text, $hide_smilies = FALSE, $is_signature = FALSE )
{
$this->text = $text;
$this->_preparse_bbcode();
$this->_parse_message($hide_smilies );

echo '<pre>';
echo $this->error, '<br/>';
print_r($this->errors);
echo '</pre>';
return $this->text;
}
}
?>
acceder au document