/** * This program 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 3 of the License, or * (at your option) any later version. * * This program 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, see <http://www.gnu.org/licenses/>. * * @category Zend * @package Zend_Filter * @copyright Copyright (c) 2007 Grummfy (http://www.grummfy.com) * @license http://www.gnu.org/licenses/lgpl.html LGPL v2 * @version v0.2 10/08/2007 Grummfy */ /** * @see Zend_Filter_Interface */ require_once 'Zend/Filter/Interface.php'; /** * @category Zend * @package Zend_Filter * @copyright Copyright (c) 2007 Grummfy (http://www.grummfy.com) * @license http://www.gnu.org/licenses/lgpl.html LGPL v2 */ class Zend_Filter_Bbcode implements Zend_Filter_Interface { protected $_config = array( 'url_limit' => 40, ); public function __construct($config = null) { if (!is_null($config) && is_array($config)) foreach ($this->_config as $k => $v) { $this->_config[ $k ] = (isset($config[ $k ]))?$config[ $k ]:$this->_config[ $k ]; } } /** * Defined by Zend_Filter_Interface * * Returns the string $text, when bbcode are replace by html code * * @param string $text * @return string */ public function filter($text) { //removing /r because it's bad! $text = str_replace("\r", '', $text); // first [nobbcode][/nobbcode] -> don't interpret bbcode $this->_parseBbcodeNobbcode($text); // parse strange bbcode, before other bbcode $this->_parseBbcodeCode($text); $this->_parseBbcodeQuote($text); $this->_parseBbcodeList($text); // easy bbcode replacement //[br] $this->_parseBbcodeBr($text); // [i]txt[/i] $this->_parseSimpleBbcode('i', '<em>$1</em>', $text); // [u]txt[/u] $this->_parseSimpleBbcode('u', '<u>$1</u>', $text); // [b]txt[/b] $this->_parseSimpleBbcode('b', '<strong>$1</strong>', $text); // [del]txt[/del] & [strike]txt[/strike] $this->_parseSimpleBbcode('del', '<del>$1</del>', $text); $this->_parseSimpleBbcode('strike', '<del>$1</del>', $text); // [color=color]txt[/color] $this->_parseParamBbcode('color', '([a-zA-Z]*|\#?[0-9a-fA-F]{6})', '<span style="color: $1">$2</span>', $text); // [bgcolor=color]txt[/bgcolor] $this->_parseParamBbcode('bgcolor', '([a-zA-Z]*|\#?[0-9a-fA-F]{6})', '<span style="background-color: $1">$2</span>', $text); // [align=(center|left|right)][/align] $this->_parseParamBbcode('align', '(center|left|right|justify){1}', '<div style="text-alignement: $1">$2</div>', $text); // [size=$size][/size] $this->_parseParamBbcode('size', '([0-9].*)', '<span style="font-size: $1">$2</span>', $text); $this->_parseBbcodeEmail($text); $this->_parseBbcodeUrl($text); $this->_parseBbcodeImg($text); return $text; } /** * parse bbcode corespondig to $pattern inside $text and replace with $replace * * @param string $pattern * @param string $replace * @param string $text * @uses preg_replace */ protected function _replaceLoop($pattern, $replace, &$text) { while (preg_match($pattern, $text)) { $text = preg_replace($pattern, $replace, $text); } } protected function _parseSimpleBbcode($tag, $replace, &$text) { $this->_replaceLoop('#\[' . $tag . '\](.*?)\[/' . $tag . '\]#si', $replace, $text); } protected function _parseParamBbcode($tag, $param, $replace, &$text) { $this->_replaceLoop('#\[' . $tag . '=' . $param . '\](.*?)\[/' . $tag . '\]#si', $replace, $text); } // // Function to parse bbcode // // [br] protected function _parseBbcodeBr(&$text) { $text = preg_replace('#\[br\]#i', ' <br />', $text); } // [img]http://www.site.tld/image.png[/img] protected function _parseBbcodeImg(&$text) { $text = preg_replace('#\[img\](.*?)\[/img\]#sei', '\'<img src="$1" alt="\' . htmlspecialchars(\'$1\') . \'" />\'', $text); } /** * Execute *before* other bbcode parse! * [nobbcode]txt[/nobbcode] * @param unknown_type $text * @return unknown */ protected function _parseBbcodeNobbcode(&$text) { $text = preg_replace('#\[nobbcode\](.+?)\[/nobbcode\]#sie', '\'<div class="bbcode_nobbcode">\' . strtr(\'$1\', array(\'[\' => \'[\', \']\' => \']\')) . \'</div>\'', $text); } // [url]url[/url] & [url=url]url txt[/url] protected function _parseBbcodeUrl(&$text) { $text = preg_replace('#\[url\]([^ \"\t\n\r<]*?)\[/url\]#ei', '$this->_encodeUrl(\'$1\', \'\', $this->_config[\'url_limit\'])', $text); $text = preg_replace('#\[url=([^ \"\t\n\r<]*?)\](.*?)\[/url\]#sei', '$this->_encodeUrl(\'$1\', \'$2\', $this->_config[\'url_limit\'])', $text); } // [email]email[/email] & [email=email]email txt[/email] protected function _parseBbcodeEmail(&$text) { $this->_replaceLoop('#\[email\]([^\[]*?)\[/email\]#sei', '$this->_encodeEmail(\'$1\')', $text); $this->_replaceLoop('#\[email=([^\[]*?)\](.*?)\[/email\]#sei', '$this->_encodeEmail(\'$1\', \'$2\')', $text); } // [code]txt[/code] & [code=language]txt[/code] protected function _parseBbcodeCode(&$text) { $text = preg_replace_callback('#\n?\[code(=[a-zA-Z0-9]*?)?\](.+?)\[/code\]\n?#is', array($this, '_cbParseBbcodeCode'), $text); } protected function _cbParseBbcodeCode($match) { $pattern = array("\n", "\t", ' ', '[', ']', ')', '(', '<', '>'); $replace = array('<br />', ' ', ' ', '[', ']', ')', '(', '<', '>'); $text = str_replace($pattern, $replace, $match[2]); $code = ''; if ($match[1] != '') { $code = '_' . substr($match[1], 1); } return '<div class="bbcode_code' . $code . '">' . $text . '</div>'; } protected function _parseBbcodeList(&$text) { $pattern = '#\n?\[list=?(greek|square|circle|disc|I|i|A|a|1)?\](.+?)\[/list\]\n?#is'; while (preg_match($pattern, $text)) { $text = preg_replace_callback($pattern, array($this, '_cbParseBbcodeList') , $text); } } protected function _parseBbcodeQuote(&$text) { $this->_replaceLoop('#\n?\[quote\](.*)\[/quote\]\n?#mSi', '<blockquote>$1</blockquote>', $text); $this->_replaceLoop('#\n?\[quote=(.*)\](.*)\[/quote\]\n?#mSi', '<blockquote><h5>$1</h5>$2</blockquote>', $text); } protected function _encodeUrl($url, $txt = '', $limit = 40) { // @TODO test url and check javascrip and other bad stuf $url_txt = ($txt != '')?$txt:$url; if ($limit > 10 && strlen($url_txt) > $limit) $url_txt = substr($url_txt, 0, $limit - 10) . '…' . substr($url_txt, -10); return '<a href="' . $url . '">' . $url_txt . '</a>'; } protected function _encodeEmail($email, $txt = '') { if (preg_match('#^\w([-_.]?\w)*@\w([-_.]?\w)*\.([a-z]{2,4})$#', $email)) { //mini anti robots $new = ''; $len = strlen($email); for ($i = 0; $i < $len; $i++) { $new .= '&#x' . bin2hex($email{$i}) . ';'; } //formating email mailto. return '<a href="mailto:' . $new . '">' . ($txt!=''?$txt:$new) . '</a>'; } return $email; } protected function _cbParseBbcodeList($match) { $text = '<ul>'; $text_end = '</ul>'; if ($match[1] != '') { switch ($match[1]) { case 'disk': case 'circle': case 'square': $text = '<ul type="' . $match[1] . '">'; break; case 'i': case 'I': case 'A': case 'a': case '1': $text = '<ol type="' . $match[1] . '">'; $text_end = '</ol>'; break; } } $this->_replaceLoop('#\n?(\[\*\](.*))\n?#is', '<li>$2</li>', $match[2]); $text .= $match[2]; return $text . $text_end; } }