* Invision Power Services * IP.Board v3.4.6 * Custom bbcode plugin interfaces * Last Updated: $Date: 2013-10-21 09:41:15 -0400 (Mon, 21 Oct 2013) $ * * * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/company/standards.php#license * @package IP.Board * @link http://www.invisionpower.com * @version $Rev: 12389 $ */ interface bbcodePlugin { /** * Method that is run before the content is stored in the database * You are responsible for ensuring you mark the replaced text appropriately so that you * are able to unparse it, if you wish to have bbcode parsed on save * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function preDbParse( $txt ); /** * Method that is run before the content is displayed to the user * This is the safest method of parsing, as the original submitted text is left in tact. * No markers are necessary if you use parse on display. * * @access public * @param string $txt BBCode/parsed text from database to be displayed * @return string Formatted content, ready for display */ public function preDisplayParse( $txt ); /** * Method that is run before the content is placed into an editor for editing * If you use "parse on display" you may simply return $txt * * @access public * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ public function preEditParse( $txt ); } /** *
 * Invision Power Services
 * IP.Board v3.4.6
 * BBCode parser: default custom bbcodes: img, quote, list, size, member, media, url, snapback
 * Last Updated: $Date: 2013-10-21 09:41:15 -0400 (Mon, 21 Oct 2013) $
 * 
* * @author $author$ * @copyright (c) 2001 - 2009 Invision Power Services, Inc. * @license http://www.invisionpower.com/company/standards.php#license * @package IP.Board * @link http://www.invisionpower.com * @version $Rev: 12389 $ * */ class bbcode_parent_class { /** * Current position in the text document * * @access protected * @var integer */ protected $cur_pos = 0; /** * Stored position of ending quote tag * * @access protected * @var integer */ protected $end_pos = 0; /** * Error message * * @access public * @var string */ public $error = ''; /** * Warning message * * @access public * @var string */ public $warning = ''; /** * This bbcode's data * * @access protected * @var integer */ protected $_bbcode = array(); /** * Registry object * * @access protected * @var object */ protected $registry; /** * Database object * * @access protected * @var object */ protected $DB; /** * Settings object * * @access protected * @var object */ protected $settings; /** * Request object * * @access protected * @var object */ protected $request; /** * Language object * * @access protected * @var object */ protected $lang; /** * Member object * * @access protected * @var object */ protected $member; protected $memberData; /** * Cache object * * @access protected * @var object */ protected $cache; protected $caches; /** * Core bbcode class * * @access protected * @var object */ protected $_parentBBcode; /** * Current bbcode * * @access protected * @var string */ protected $currentBbcode; /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { /* Make object */ $this->registry = $registry; $this->DB = $this->registry->DB(); $this->settings =& $this->registry->fetchSettings(); $this->request =& $this->registry->fetchRequest(); $this->lang = $this->registry->getClass('class_localization'); $this->member = $this->registry->member(); $this->memberData =& $this->registry->member()->fetchMemberData(); $this->cache = $this->registry->cache(); $this->caches =& $this->registry->cache()->fetchCaches(); $this->_parentBBcode = $_parent; /* Retrieve bbcode data */ $bbcodeCache = $this->cache->getCache('bbcode'); $this->_bbcode = $bbcodeCache[ $this->currentBbcode ]; } /** * Method that is run before the content is stored in the database * You are responsible for ensuring you mark the replaced text appropriately so that you * are able to unparse it, if you wish to have bbcode parsed on save * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function preDbParse( $txt ) { return $txt; } /** * Method that is run before the content is displayed to the user * This is the safest method of parsing, as the original submitted text is left in tact. * No markers are necessary if you use parse on display. * * @access public * @param string $txt BBCode/parsed text from database to be displayed * @return string Formatted content, ready for display */ public function preDisplayParse( $txt ) { $this->cur_pos = 0; $this->end_pos = 0; $this->error = ''; return $this->_replaceText( $txt ); } /** * Method that is run before the content is placed into an editor for editing * If you use "parse on display" you may simply return $txt * * @access public * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ public function preEditParse( $txt ) { $this->error = ''; return $txt; } /** * Retrieves the tags used for this bbcode, including aliases * * @access public * @return array Array of tags to check */ protected function _retrieveTags() { $_tags = array( $this->_bbcode['bbcode_tag'] ); //----------------------------------------- // We'll also need to check for any aliases //----------------------------------------- if( $this->_bbcode['bbcode_aliases'] ) { $aliases = explode( ',', trim($this->_bbcode['bbcode_aliases']) ); if( is_array($aliases) AND count($aliases) ) { foreach( $aliases as $alias ) { $_tags[] = trim($alias); } } } return $_tags; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_sharedmedia extends bbcode_parent_class implements bbcodePlugin { /** * Store plugins we've instantiated * * @var array */ protected $plugins = array(); /** * Constructor * * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'sharedmedia'; parent::__construct( $registry, $_parent ); } /** * We use preDbParse to verify you have permission to share the items you are sharing * * @access public * @param string $txt BBCode text from submission to be stored in database * @return string Formatted content, ready for display */ public function preDbParse( $txt ) { $_ignored = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_checkPostingPermissions' ), $txt ); return $txt; } /** * We use this to check for permissions * * @param array preg_replace_callback Matches * @return @e string */ protected function _checkPostingPermissions( $matches ) { $txt = trim( $matches[1] ); $app = trim( $matches[2] ); $plugin = trim( $matches[3] ); if( !$txt OR !$app OR !$plugin ) { return ''; } if( !isset($this->plugins[ $app ][ $plugin ]) ) { if( is_file( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php' ) ) { $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php', 'plugin_' . $app . '_' . $plugin, $app ); $this->plugins[ $app ][ $plugin ] = new $classToLoad( $this->registry ); } } if( isset($this->plugins[ $app ][ $plugin ]) ) { if( $error = $this->plugins[ $app ][ $plugin ]->checkPostPermission( $matches[4] ) ) { $this->error = $error; } } //----------------------------------------- // Return the original output (we aren't replacing, just verifying permissions) //----------------------------------------- return $txt; } /** * Do the actual replacement * * @param string $txt Parsed text from database to be edited * @return @e string */ protected function _replaceText( $txt ) { $txt = preg_replace_callback( '#(\[sharedmedia=(.+?):(.+?):(.+?)\])#is' , array( $this, '_parseMedia' ), $txt ); return $txt; } /** * Callback for shared media preg_replace call * * @param array preg_replace_callback Matches * @return @e string */ protected function _parseMedia( $matches ) { $txt = trim( $matches[1] ); $app = trim( $matches[2] ); $plugin = trim( $matches[3] ); $_orig = $txt; if( !$txt OR !$app OR !$plugin ) { return ''; } if( !isset($this->plugins[ $app ][ $plugin ]) ) { if( is_file( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php' ) ) { $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( $app ) . '/extensions/sharedmedia/plugin_' . $plugin . '.php', 'plugin_' . $app . '_' . $plugin, $app ); $this->plugins[ $app ][ $plugin ] = new $classToLoad( $this->registry ); } } if( isset($this->plugins[ $app ][ $plugin ]) ) { $txt = $this->plugins[ $app ][ $plugin ]->getOutput( $matches[4] ); } //----------------------------------------- // Return the replaced output //----------------------------------------- return $txt; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_img extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'img'; parent::__construct( $registry, $_parent ); } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { $_tags = $this->_retrieveTags(); foreach( $_tags as $_tag ) { //----------------------------------------- // Start building open/close tag //----------------------------------------- $open_tag = '[' . $_tag . ']'; $close_tag = '[/' . $_tag . ']'; //----------------------------------------- // Infinite loop catcher //----------------------------------------- $_iteration = 0; //----------------------------------------- // Doz I can haz opin tag? Loopy loo //----------------------------------------- while( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { //----------------------------------------- // Stop infinite loops //----------------------------------------- if( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration++; //----------------------------------------- // Grab the new position to jump to //----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; //----------------------------------------- // No closing tag //----------------------------------------- if( stripos( $txt, $close_tag, $new_pos ) === false ) { break; } //----------------------------------------- // Grab the content //----------------------------------------- $_content = substr( $txt, ($this->cur_pos + strlen($open_tag)), (stripos( $txt, $close_tag, $this->cur_pos ) - ($this->cur_pos + strlen($open_tag))) ); //----------------------------------------- // If this is a single tag, that's it //----------------------------------------- if( $_content ) { /* Trying to sneak in another URL to auto parse? */ preg_match_all( '#http(s)?://#i', $_content, $_match ); if ( count( $_match[0] ) > 1 ) { /* Make safe */ $txt = preg_replace( "#(http|https|news|ftp)://#i", "\\1://", $txt ); return $txt; } $txt = substr_replace( $txt, $this->_buildOutput( $_content ), $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } else { $txt = substr_replace( $txt, '', $this->cur_pos, (stripos( $txt, $close_tag, $this->cur_pos ) + strlen($close_tag) - $this->cur_pos) ); } //----------------------------------------- // And reset current position to end of open tag //----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; //$new_pos; if( $this->cur_pos > strlen($txt) ) { //----------------------------------------- // Need to reset for next "tag" //----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param array $content Image URL to link to * @return string Content to replace bbcode with */ protected function _buildOutput( $content ) { $content = trim($content); //----------------------------------------- // Too many images? //----------------------------------------- $existing = $this->cache->getCache( '_tmp_bbcode_images', false ); $existing = intval($existing) + 1; if ( $this->settings['max_images'] AND $this->caches['_tmp_section'] != 'signatures' ) { if ($existing > $this->settings['max_images']) { $this->error = 'too_many_img'; return $content; } } $this->cache->updateCacheWithoutSaving( '_tmp_bbcode_images', $existing ); //----------------------------------------- // Some security checking //----------------------------------------- if ( IPSText::xssCheckUrl( $content ) !== TRUE ) { return $content; } foreach( $this->cache->getCache('bbcode') as $bbcode ) { $_tags = $this->_retrieveTags(); foreach( $_tags as $tag ) { if ( stripos( $content, '[' . $tag ) !== false ) { return $content; } } } //----------------------------------------- // Allowed type? //----------------------------------------- if ( $this->settings['img_ext'] ) { $path = @parse_url( html_entity_decode( $content ), PHP_URL_PATH ); $pieces = explode( '.', $path ); $ext = array_pop( $pieces ); $ext = strtolower( $ext ); if( !in_array( $ext, explode( ',', str_replace( '.', '', strtolower($this->settings['img_ext']) ) ) ) ) { $this->error = 'invalid_ext'; return $content; } } //----------------------------------------- // URL filtering? //----------------------------------------- if ( $this->settings['ipb_use_url_filter'] ) { $list_type = $this->settings['ipb_url_filter_option'] == "black" ? "blacklist" : "whitelist"; if( $this->settings['ipb_url_' . $list_type ] ) { $list_values = array(); $list_values = explode( "\n", str_replace( "\r", "", $this->settings['ipb_url_' . $list_type ] ) ); if( $list_type == 'whitelist' ) { $list_values[] = "http://{$_SERVER['HTTP_HOST']}/*"; } if ( count( $list_values ) ) { $good_url = 0; foreach( $list_values as $my_url ) { if( !trim($my_url) ) { continue; } $my_url = preg_quote( $my_url, '/' ); $my_url = str_replace( '\*', "(.*?)", $my_url ); if ( $list_type == "blacklist" ) { if( preg_match( '/' . $my_url . '/i', $content ) ) { $this->error = 'domain_not_allowed'; return $content; } } else { if ( preg_match( '/' . $my_url . '/i', $content ) ) { $good_url = 1; } } } if ( ! $good_url AND $list_type == "whitelist" ) { $this->error = 'domain_not_allowed'; return $content; } } } } return "{$this->lang->words["; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_quote extends bbcode_parent_class implements bbcodePlugin { /**#@+ * Quote tracking * * @access protected * @var int */ protected $quote_open = 0; protected $quote_closed = 0; protected $quote_error = 0; /**#@-*/ /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'quote'; parent::__construct( $registry, $_parent ); } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { /* Clean up some dickery */ $txt = preg_replace( '#\[quote([^\]]+?)?\]\](\s+?)?\[/url\](\s+?)?\[/quote\]#si', '', $txt ); $orig = $txt; $txt = preg_replace_callback( '#(\[quote([^\]]+?)?\].*\[/quote\])#is' , array( $this, '_parseQuote' ), $txt ); if( $this->error ) { return $orig; } if( stripos( $txt, '[quote' ) !== false OR stripos( $txt, '[/quote]' ) !== false ) { $this->error = 'quote_mismatch'; return $orig; } return $txt; } /** * Build the actual output to show * * @access protected * @param array $options [Optional] Quote options * @return string Content to replace bbcode with */ protected function _buildOutput( $options=array() ) { //----------------------------------------- // Build output and return it //----------------------------------------- if ( $options['collapse'] ) { if ( $options['collapse'] == '1' ) { $options['collapse'] = $this->lang->words['bbc_quote']; } $eqid = rand( 1, 1000000 ); $output = << OUTPUT; } else { $output = "

"; } $snapback = ''; if( $options['post'] ) { $snapback = "" . $this->registry->output->getReplacement( 'snapback' ) . ""; } if( $options['name'] OR $options['date'] OR $options['timestamp'] ) { // sort timestamp if ( $options['timestamp'] AND strlen( $options['timestamp'] ) == 10 AND ( intval($options['timestamp']) == $options['timestamp'] ) ) { if ( $this->settings['cc_on'] ) { /* Add for parsing */ $options['date'] = ''; } else { $options['date'] = $this->registry->getClass('class_localization')->getDate( $options['timestamp'], 'LONG' ); } } if( $options['name'] AND $options['date'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_full_cite'], $options['name'], $options['date'] ) ; } else if( $options['name'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_name_cite'], $options['name'] ) ; } else if( $options['date'] ) { $output .= $snapback . sprintf( $this->lang->words['bbc_date_cite'], $options['date'] ) ; } } elseif ( $options['collapse'] ) { $output .= $snapback . $options['collapse'] . ' ' . $this->lang->words['bbc_quote_collapsed']; } else { $output .= $snapback . $this->lang->words['bbc_quote']; } $output .= "

"; $output .= '
'; $output .= $options['collapse'] ? "", $txt, $this->quote_closed ); //----------------------------------------- // Replace the quote tag //----------------------------------------- $txt = preg_replace_callback( '#\[quote([^\]]+?)?\]#i', array( $this, '_replaceQuoteTag' ), $txt ); //----------------------------------------- // Newlines //----------------------------------------- //$txt = str_replace( "\n", "
", $txt ); $txt = str_replace( array( "

", "
~~~~~_____~~~~~" ), "
", $txt ); //----------------------------------------- // Swap name replacement (_makeNameSafe) back //----------------------------------------- $txt = str_replace( ''', "'", $txt ); //----------------------------------------- // Turn attachments into links // Prevents em from breaking on other pages //----------------------------------------- preg_match_all( "#
(.+?)
#ims", $txt, $_outerMatches ); foreach( $_outerMatches[1] as $_outerMatch ) { preg_match_all( '#\[attachment=(.+?):(.+?)\]#', $_outerMatch, $_matches ); if( is_array( $_matches[1] ) && count( $_matches[1] ) ) { foreach( $_matches[1] as $idx => $attach_id ) { $txt = str_replace( "[attachment={$attach_id}:{$_matches[2][$idx]}]", $this->registry->getClass('output')->getReplacement('post_attach_link') . " {$_matches[2][$idx]}", $txt ); } } } //----------------------------------------- // If open and close tags match, we're good. // Otherwise, return an error. //----------------------------------------- if ( ( $this->quote_open == $this->quote_closed ) and ( $this->quote_error == 0 ) ) { return $txt; } else { $this->error = 'quote_mismatch'; return $_orig; } } /** * Callback for triming quote * * @access protected * @param array preg_replace_callback Matches * @return string Output */ protected function _trimQuote( $matches ) { $txt = $matches[2]; $extra = IPSText::stripUrls( $matches[1] ); if( $txt == "" ) { return "[quote][/quote]"; } else { $txt = trim( $txt ); return "[quote{$extra}]{$txt}[/quote]"; } } /** * Make the quoted content safe for regex parsing * * @access protected * @param array preg_replace_callback Matches * @return string Output */ protected function _makeQuoteSafe( $matches ) { //----------------------------------------- // INIT //----------------------------------------- $begin = $matches[1]; $end = $matches[3]; $txt = $matches[2]; //----------------------------------------- // Sort name //----------------------------------------- $txt = str_replace( "+", "+" , $txt ); $txt = str_replace( "-", "-" , $txt ); $txt = str_replace( ":", ":" , $txt ); $txt = str_replace( "[", "[" , $txt ); $txt = str_replace( "]", "]" , $txt ); $txt = str_replace( ")", ")" , $txt ); $txt = str_replace( "(", "(" , $txt ); $txt = str_replace( "'", "'" , $txt ); return $begin . IPSText::getTextClass('bbcode')->xssHtmlClean( IPSText::getTextClass('bbcode')->stripAllTags( IPSText::stripUrls( $txt ) ) ) . $end; } /** * Make the name used for the quote safe for regex parsing * * @access protected * @param array preg_replace_callback Matches * @return string Output * @note This looks complicated (and is somewhat) but it's done this way to try to quote correctly users with ' in their names * @link http://community.invisionpower.com/tracker/issue-18588-bbcode-mistake-when-quoting-users-with-a-in-their-name */ protected function _makeNameSafe( $matches ) { $quote = $matches[1]; $name = $matches[2]; $next = ''; if( strpos( $name, 'post=' ) !== false ) { $next = substr( $name, strpos( $name, 'post=' ) ); $name = substr( $name, 0, strpos( $name, 'post=' ) -1 ); } if( strpos( $name, 'date=' ) !== false ) { $next = $next . ' ' . substr( $name, strpos( $name, 'date=' ) ); $name = substr( $name, 0, strpos( $name, 'date=' ) -1 ); } if( strpos( $name, 'timestamp=' ) !== false ) { $next = $next . ' ' . substr( $name, strpos( $name, 'timestamp=' ) ); $name = substr( $name, 0, strpos( $name, 'timestamp=' ) -1 ); } $name = substr( $name, 0, -(strlen($quote)) ); # Squeeze past the parser... $name = str_replace( array( ''', "'" ), "'", $name ); $_last = $next ? ' ' . $next : ''; return 'name=' . $quote . IPSText::getTextClass('bbcode')->xssHtmlClean( IPSText::getTextClass('bbcode')->stripAllTags( IPSText::stripUrls( $name ) ) ) . $quote . $_last . ']'; } /** * Replace the quote tag * * @access protected * @param array preg_replace_callback Matches * @return string Output */ protected function _replaceQuoteTag( $matches ) { //----------------------------------------- // INIT //----------------------------------------- $extra = str_replace( ''', "'", $matches[1] ); $post_id = 0; $date = ''; $timestamp = 0; $name = ''; //----------------------------------------- // Inc.. //----------------------------------------- $this->quote_open++; //----------------------------------------- // Post? //----------------------------------------- preg_match( '#post=([\"\']|"|&\#039;|&\#39;)?(\d+)(\\1)?#', $extra, $match ); if ( isset($match[2]) AND intval( $match[2] ) ) { $post_id = intval( $match[2] ); } //----------------------------------------- // Name? //----------------------------------------- preg_match( '#name=([\"\']|"|&\#039;|&\#39;)(.*?)(\\1)\s?(date|post)?#is', $extra, $match ); if ( !empty($match[2]) ) { $name = $match[2] ? $this->_makeQuoteSafe( array( 2 => $match[2] ) ) : '-'; } //----------------------------------------- // Date? //----------------------------------------- preg_match( '#date=([\"\']|"|&\#039;|&\#39;)(.*?)(\\1)#', $extra, $match ); if ( !empty($match[2]) ) { $date = $this->_makeQuoteSafe( array( 2 => $match[2] ) ); } //----------------------------------------- // Timestamp? //----------------------------------------- preg_match( '#timestamp=([\"\']|"|&\#039;|&\#39;)(.*?)(\\1)#', $extra, $match ); if ( !empty($match[2]) ) { $timestamp = intval( $match[2] ); } //----------------------------------------- // Collapse? //----------------------------------------- preg_match( '#collapse=([\"\']|"|&\#039;|&\#39;)(.*?)(\\1)#', $extra, $match ); if ( !empty($match[2]) ) { $collapse = $match[2]; } return $this->_buildOutput( array( 'name' => $name, 'date' => $date, 'post' => $post_id, 'timestamp' => $timestamp, 'collapse' => $collapse ) ); } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_code extends bbcode_parent_main_class { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent='' ) { $this->currentBbcode = 'code'; $this->_parent = $_parent; parent::__construct( $registry, $_parent ); } /** * Check and make safe embedded codes * @param array $matches */ protected function _cleanCodeBoxes( $matches ) { $txt = $matches[0]; $map = array(); /* Load parser */ $classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/text/parser.php', 'classes_text_parser' ); $parser = new $classToLoad(); /* Fetch paired opening and closing tags */ $data = $parser->getTagPositions( $txt, 'code', array( '[', ']') ); foreach( $data['open'] as $id => $val ) { $o = $data['open'][ $id ]; $c = $data['close'][ $id ] - $o; $slice = substr( $txt, $o, $c ); if ( stristr( $slice, '[code' ) ) { $slice = str_ireplace( '[code', '[code', $slice ); } if ( stristr( $slice, '[/code]' ) ) { $slice = str_ireplace( '[/code]', '[/code]', $slice ); } $txt = substr_replace( $txt, $slice, $o, $c ); } return $txt; } /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { /* Convert old indent tags over */ if ( stristr( $txt, '[code]' ) ) { $txt = str_ireplace( '[code]', '[code=auto:0]', $txt ); } //print 'x'; exit(); $txt = preg_replace_callback( '#(\[code\].*\[/code\])#is', array( $this, '_cleanCodeBoxes' ), $txt ); $this->_bbcode['bbcode_useoption'] = true; $this->_bbcode['bbcode_optional_option'] = true; $_tags = $this->_retrieveTags(); foreach( $_tags as $_tag ) { // ----------------------------------------- // Infinite loop catcher // ----------------------------------------- $_iteration = 0; // ----------------------------------------- // Start building open/close tag // ----------------------------------------- $open_tag = '[' . $_tag; $close_tag = '[/' . $_tag . ']'; // ----------------------------------------- // Doz I can haz opin tag? Loopy loo // ----------------------------------------- while ( ( $this->cur_pos = stripos( $txt, $open_tag, $this->cur_pos ) ) !== false ) { // ----------------------------------------- // Stop infinite loops // ----------------------------------------- if ( $_iteration > $this->settings['max_bbcodes_per_post'] ) { break; } $_iteration ++; $open_length = strlen( $open_tag ); // ----------------------------------------- // Extract the option (like surgery) // ----------------------------------------- $_option = ''; if ( $this->_bbcode['bbcode_useoption'] ) { // ----------------------------------------- // Is option optional? // ----------------------------------------- if ( $this->_bbcode['bbcode_optional_option'] ) { // ----------------------------------------- // Does we haz it? // ----------------------------------------- if ( substr( $txt, $this->cur_pos + strlen( $open_tag ), 1 ) == '=' ) { $open_length += 1; // ----------------------------------------- // This is here to try to capture urls with // [ and ] in them, only works if enclosed in quotes // ----------------------------------------- $cur_content = ''; if ( substr( $txt, $this->cur_pos + $open_length, 6 ) == '"' ) { /* * Skip the bbocde if there is more than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 6, ( $end_pos - ( $this->cur_pos + $open_length + 6 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 6, ( $end_pos - ( $this->cur_pos + $open_length + 6 ) ) ); } else if ( substr( $txt, $this->cur_pos + $open_length, 5 ) == "'" ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, ''', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, ''', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 5, ( $end_pos - ( $this->cur_pos + $open_length + 5 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 5, ( $end_pos - ( $this->cur_pos + $open_length + 5 ) ) ); } // ----------------------------------------- // Need this because HTML on converts the // entities back to quote/apos // ----------------------------------------- else if ( substr( $txt, $this->cur_pos + $open_length, 1 ) == '"' ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, '"', $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) ? stripos( $txt, '"', $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); } else if ( substr( $txt, $this->cur_pos + $open_length, 1 ) == "'" ) { /* * Skip the bbocde if there is mroe than 2 * quotes. Bug #21161 */ if ( strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) < 1 or ( substr_count( $txt, "'", $this->cur_pos, strlen( substr( $txt, $this->cur_pos, stripos( $txt, ']', $this->cur_pos ) - $this->cur_pos ) ) ) ) > 2 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } $end_pos = stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) ? stripos( $txt, "'", $this->cur_pos + $open_length + 1 ) : strpos( $txt, ']', $this->cur_pos + $open_length + 1 ); $cur_content = substr( $txt, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); $new_content = str_replace( '[', '%5B', str_replace( ']', '%5D', $cur_content ) ); $txt = substr_replace( $txt, $new_content, $this->cur_pos + $open_length + 1, ( $end_pos - ( $this->cur_pos + $open_length + 1 ) ) ); } $_option = substr( $txt, $this->cur_pos + $open_length, ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) ); } // ----------------------------------------- // If not, [u] != [url] (for example) // ----------------------------------------- else if ( ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } } // ----------------------------------------- // No? Then just grab it // ----------------------------------------- else { $open_length += 1; $_option = substr( $txt, $this->cur_pos + $open_length, ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) ); } } // ----------------------------------------- // [img] != [i] (for example) // ----------------------------------------- else if ( ( strpos( $txt, ']', $this->cur_pos ) - ( $this->cur_pos + $open_length ) ) !== 0 ) { $this->cur_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; continue; } // ----------------------------------------- // Grab the new position to jump to // ----------------------------------------- $new_pos = strpos( $txt, ']', $this->cur_pos ) ? strpos( $txt, ']', $this->cur_pos ) : $this->cur_pos + 1; // ----------------------------------------- // No closing tag // ----------------------------------------- if ( stripos( $txt, $close_tag, $new_pos ) === false ) { break; } // ----------------------------------------- // Grab the content // ----------------------------------------- $_content = substr( $txt, ( $this->cur_pos + $open_length + strlen( $_option ) + 1 ), ( ( stripos( $txt, $close_tag, $this->cur_pos + $open_length + strlen( $_option ) + 1 ) ) - ( $this->cur_pos + $open_length + strlen( $_option ) + 1 ) ) ); // ----------------------------------------- // If this is a single tag, that's it // @link: // http://forums.invisionpower.com/index.php?autocom=tracker&showissue=11909 // ----------------------------------------- if ( $_content or $_content === '0' ) { if ( $this->_buildOutput( $_content, $_option ? $_option : $_content ) ) { $txt = substr_replace( $txt, $this->_buildOutput( $_content, $_option ? $_option : $_content ), $this->cur_pos, ( stripos( $txt, $close_tag, $this->cur_pos ) + strlen( $close_tag ) - $this->cur_pos ) ); } } else { $txt = substr_replace( $txt, '', $this->cur_pos, ( stripos( $txt, $close_tag, $this->cur_pos ) + strlen( $close_tag ) - $this->cur_pos ) ); } // ----------------------------------------- // And reset current position to end of open tag // ----------------------------------------- $this->cur_pos = stripos( $txt, $open_tag ) ? stripos( $txt, $open_tag ) : $this->cur_pos + 1; // $new_pos; if ( $this->cur_pos > strlen( $txt ) ) { // ----------------------------------------- // Need to reset for next "tag" // ----------------------------------------- $this->cur_pos = 0; break; } } } return $txt; } /** * Build the actual output to show * * @access protected * @param string $content Text * @param string $option Text * @return string Content to replace bbcode with */ protected function _buildOutput( $content, $option ) { //----------------------------------------- // Strip the optional quote delimiters //----------------------------------------- $option = intval( $option ); $content = trim( $content ); $content = preg_replace( '#(]+?)?>)#i', '', $content ); $content = trim( $content ); /* Make contents of code boxes safe */ //$content = preg_replace( '/&(#[0-9]{3,4}|[a-zA-Z]{2,5});/', '&\1;', $content ); $content = str_replace( '<', '<', $content ); $content = str_replace( '>', '>', $content ); $lineNums = 1; $langAdd = ''; if ( $option ) { list( $lang, $lineNums ) = explode( ':', $option ); } if ( $lang ) { $langAdd = ' _lang-' . trim( $lang ); } /* We use underscores so the code is not highlighted when using CKEditor */ return "
{$content}
"; } } //---------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------- class bbcode_list extends bbcode_parent_class implements bbcodePlugin { /** * Constructor * * @access public * @param object Registry object * @param object Parent bbcode class * @return @e void */ public function __construct( ipsRegistry $registry, $_parent=null ) { $this->currentBbcode = 'list'; parent::__construct( $registry, $_parent ); } //public function preDisplayParse($txt){parent::preDisplayParse($txt);exit;} /** * Do the actual replacement * * @access protected * @param string $txt Parsed text from database to be edited * @return string BBCode content, ready for editing */ protected function _replaceText( $txt ) { $_tags = $this->_retrieveTags(); $txt = preg_replace( '#\[/list\](\s+?)?
#is', "[/list]", $txt ); foreach( $_tags as $_tag ) { while( preg_match( "#\n?\[{$_tag}(.*?)\](.+?)\[/{$_tag}\]\n?#is" , $txt, $matches ) ) { $txt = preg_replace_callback( "#(\n){0,1}\[{$_tag}(.*?)\](.+?)\[/{$_tag}\](\n){0,1}#is", array( &$this, '_buildOutput' ), $txt ); } /*while( preg_match( "#\n?\[{$_tag}=(a|A|i|I|1)\](.+?)\[/{$_tag}\]\n?#is" , $txt ) ) { $txt = preg_replace_callback( "#(\n){0,1}\[{$_tag}=(a|A|i|I|1)\](.+?)\[/{$_tag}\](\n){0,1}#is", array( &$this, '_buildOutput' ), $txt ); }*/ } return $txt; } /** * Build the actual output to show * * @access protected * @param string $matches Array of regex matches * @return string Content to replace bbcode with */ protected function _buildOutput( $matches=array() ) { //----------------------------------------- // Make sure we have at least one list item //----------------------------------------- //if( $matches[2] AND strpos( $matches[2], '[*]' ) === false ) //{ // return preg_replace( "#\[/?list\]?#i", "", $matches[0] . $matches[2] . $matches[3] ); //} //----------------------------------------- // INIT //----------------------------------------- if( $matches[2] ) { $matches[2] = str_replace( array( '"', "'", '"', ''', ''', '=' ), '', $matches[2] ); } $types = array( 'a', 'A', 'i', 'I', '1' ); if ( in_array( $matches[2], $types ) ) { $fnl = $matches[1]; $type = $matches[2]; $txt = $matches[3]; $lnl = $matches[4]; } else { $fnl = $matches[1]; $type = ''; $txt = $matches[3]; $lnl = $matches[4]; } if ( !$txt ) { return; } //----------------------------------------- // No br tags, br tags bad //----------------------------------------- $txt = str_replace( "\n", "", $txt ); $txt = str_replace( array( '
', '
'), "\n", $txt ); $txt = str_replace( "[/list]\n[*]", "[/list][*]", $txt ); $return = ''; if ( ! $type ) { $return = $fnl . "
    " . $this->_listItem($txt) . "
" . $lnl; } else { $_cssClass = "decimal"; switch( $type ) { case 'a': $_cssClass = "lower-alpha"; break; case 'A': $_cssClass = "upper-alpha"; break; case 'i': $_cssClass = "lower-roman"; break; case 'I': $_cssClass = "upper-roman"; break; } // if( !$this->caches['_tmp_bbcode_isForRte'] ) // { $return = $fnl . "
    " . $this->_listItem($txt) . "
" . $lnl; // } // else // { //$return = $fnl . "
    " . $this->_listItem($txt) . "
" . $lnl; //} } /* Need to remove any extra closing
  • tags. bug #20327 */ $return = preg_replace( '/