1167 lines
39 KiB
PHP
1167 lines
39 KiB
PHP
<?php
|
|
/**
|
|
* @brief Legacy Text Parser
|
|
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
|
* @copyright (c) Invision Power Services, Inc.
|
|
* @license https://www.invisioncommunity.com/legal/standards/
|
|
* @package Invision Community
|
|
* @since 12 Jun 2013
|
|
*/
|
|
|
|
namespace IPS\Text;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Legacy Text Parser
|
|
*/
|
|
class _LegacyParser
|
|
{
|
|
/**
|
|
* Maximum number of embeds we will try.
|
|
* @note Attempting to pull too many during the rebuild process can easily time out
|
|
*/
|
|
public $maxEmbeds = 10;
|
|
|
|
/**
|
|
* Parse statically
|
|
*
|
|
* @param string $value The value to parse
|
|
* @param \IPS\Member|null $member The member posting. NULL will use currently logged in member.
|
|
* @param bool $allowHtml Allow HTML
|
|
* @param string $attachClass Key to use for attachments
|
|
* @param int $id1 ID1 to use for attachments
|
|
* @param int $id2 ID2 to use for attachments
|
|
* @param int|NULL $id3 ID3 to use for attachments
|
|
* @param string $itemClass The *item* classname (e.g. IPS\forums\Topic)
|
|
* @return string
|
|
* @see __construct
|
|
*/
|
|
public static function parseStatic( $value, $member=NULL, $allowHtml=FALSE, $attachClass=null, $id1=0, $id2=0, $id3=NULL, $itemClass=NULL )
|
|
{
|
|
$obj = new self( $member, $allowHtml, $attachClass, $id1, $id2, $id3, $itemClass );
|
|
return $obj->parse( $value );
|
|
}
|
|
|
|
/**
|
|
* @brief BBcodes
|
|
*/
|
|
public $bbcodes = NULL;
|
|
|
|
/**
|
|
* @brief Emoticons
|
|
*/
|
|
public $emoticons = NULL;
|
|
|
|
/**
|
|
* Can use plugin?
|
|
*
|
|
* @param \IPS\Member $member The member
|
|
* @param string $bbcode BBCode
|
|
* @return bool
|
|
*/
|
|
public static function canUse( \IPS\Member $member, $bbcode )
|
|
{
|
|
foreach( static::$bbcodes as $code )
|
|
{
|
|
if( $code['bbcode_tag'] == $bbcode OR ( $code['bbcode_aliases'] AND in_array( $bbcode, explode( ',', $code['bbcode_aliases'] ) ) ) )
|
|
{
|
|
if( $code['bbcode_groups'] == 'all' )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( $member->inGroup( explode( ',', $bbcode['bbcode_groups'] ) ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Member
|
|
*/
|
|
protected $member = NULL;
|
|
|
|
/**
|
|
* @brief Allow HTML
|
|
*/
|
|
protected $allowHtml = FALSE;
|
|
|
|
/**
|
|
* @brief Parser object
|
|
*/
|
|
protected $parser = NULL;
|
|
|
|
/**
|
|
* @brief Key for attachments
|
|
*/
|
|
protected $attachClass = NULL;
|
|
|
|
/**
|
|
* @brief Item classname
|
|
*/
|
|
protected $itemClass = NULL;
|
|
|
|
/**
|
|
* @brief ID1 to use for attachments
|
|
*/
|
|
protected $idOne = 0;
|
|
|
|
/**
|
|
* @brief ID2 to use for attachments
|
|
*/
|
|
protected $idTwo = 0;
|
|
|
|
/**
|
|
* @brief ID3 to use for attachments
|
|
*/
|
|
protected $idThree = 0;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param \IPS\Member|null $member The member posting. NULL will use guest.
|
|
* @param bool $allowHtml Allow HTML
|
|
* @param string $attachClass Key to use for attachments
|
|
* @param int $id1 ID1 to use for attachments
|
|
* @param int $id2 ID2 to use for attachments
|
|
* @param int|NULL $id3 ID3 to use for attachments
|
|
* @param string $itemClass The *item* classname (e.g. IPS\forums\Topic)
|
|
* @return void
|
|
*/
|
|
public function __construct( $member=NULL, $allowHtml=FALSE, $attachClass=null, $id1=0, $id2=0, $id3=NULL, $itemClass=NULL )
|
|
{
|
|
/* Get member */
|
|
$this->member = $member === NULL ? \IPS\Member::load(0) : $member;
|
|
|
|
/* Remember if we allow HTML */
|
|
$this->allowHtml = $allowHtml;
|
|
|
|
/* Set attachment data */
|
|
$this->attachClass = $attachClass;
|
|
$this->idOne = $id1;
|
|
$this->idTwo = $id2;
|
|
$this->idThree = $id3;
|
|
|
|
/* And item class */
|
|
$this->itemClass = $itemClass;
|
|
|
|
/* Grab legacy custom bbcodes */
|
|
try
|
|
{
|
|
$this->bbcodes = iterator_to_array( \IPS\Db::i()->select( '*', 'custom_bbcode' )->setKeyField( 'bbcode_tag' ) );
|
|
}
|
|
catch( \IPS\Db\Exception $e )
|
|
{
|
|
$this->bbcodes = array();
|
|
}
|
|
|
|
/* Grab emoticons */
|
|
try
|
|
{
|
|
$this->emoticons = iterator_to_array( \IPS\Db::i()->select( '*', 'core_emoticons' )->setKeyField( 'typed' ) );
|
|
}
|
|
catch( \IPS\Db\Exception $e )
|
|
{
|
|
$this->emoticons = array();
|
|
}
|
|
|
|
/* Grab a new parser object */
|
|
\IPS\Text\Parser::$requestTimeout = 1;
|
|
$this->parser = new \IPS\Text\Parser( TRUE, $this->idOne ? array( $this->idOne, $this->idTwo, $this->idThree ) : NULL, $this->member, $this->attachClass, TRUE, ( $this->allowHtml ? FALSE : TRUE ), NULL );
|
|
|
|
$updatedBbcodeTags = array_keys( $this->parser->bbcodeTags( $this->member, TRUE ) );
|
|
|
|
foreach( $updatedBbcodeTags as $tag )
|
|
{
|
|
if( isset( $this->bbcodes[ $tag ] ) AND $tag != 'media' )
|
|
{
|
|
unset( $this->bbcodes[ $tag ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse
|
|
*
|
|
* @param string $value Text to parse
|
|
* @return string
|
|
*/
|
|
public function parse( $value )
|
|
{
|
|
/* We have to fix up newlines a little bit */
|
|
$codeBlocks = array();
|
|
$value = str_replace( array( "\r\n", "\r" ), "\n", $value );
|
|
|
|
if ( ! \strstr( $value, "\n" ) && ( \stristr( $value, '<br>' ) || \stristr( $value, '<br />' ) ) )
|
|
{
|
|
$value = \str_ireplace( array( '<br>', '<br />' ), "\n", $value );
|
|
}
|
|
else
|
|
{
|
|
if ( \stristr( $value, '<br>' ) || \stristr( $value, '<br />' ) || \stristr( $value, '<p>' ) )
|
|
{
|
|
/* Protect code tags first... */
|
|
$value = preg_replace_callback( "/\[(?:code|codebox|sql|php|xml|html).*?\](.+?)\[\/(?:code|codebox|sql|php|xml|html)\]/ims", function( $match ) use ( &$codeBlocks ) {
|
|
$hash = md5( $match[0] . mt_rand() );
|
|
$codeBlocks[ $hash ] = ( \stristr( $match[0], '<br>' ) || \stristr( $match[0], '<br />' ) || \stristr( $match[0], '<p>' ) ) ? \str_replace( "\n", "", $match[0] ) : $match[0];
|
|
return $hash;
|
|
}, $value );
|
|
|
|
$value = preg_replace_callback( "/\<pre\s+?class=[\"']_prettyXprint(?:.*?)[\"']\>(.+?)\<\/pre\>/ims", function( $match ) use ( &$codeBlocks ) {
|
|
$hash = md5( $match[0] . mt_rand() );
|
|
$codeBlocks[ $hash ] = ( \stristr( $match[0], '<br>' ) || \stristr( $match[0], '<br />' ) || \stristr( $match[0], '<p>' ) ) ? \str_replace( "\n", "", $match[0] ) : $match[0];
|
|
return $hash;
|
|
}, $value );
|
|
|
|
$value = \str_replace( "\n", "", $value );
|
|
}
|
|
}
|
|
|
|
/* If we allow HTML we have to swap out entities - codeblocks are already protected at this point */
|
|
if( $this->allowHtml )
|
|
{
|
|
/* Fixes an issue with legacy posts */
|
|
$value = str_replace( '"', '"', $value );
|
|
$value = str_replace( '<', '<', $value );
|
|
$value = str_replace( '>', '>', $value );
|
|
}
|
|
|
|
$value = nl2br( $value );
|
|
|
|
/* If we had any code blocks, put them back now */
|
|
foreach( $codeBlocks as $hash => $code )
|
|
{
|
|
$value = \str_replace( $hash, $code, $value );
|
|
}
|
|
|
|
/* In case the content rebuild has ran or partially ran on this content... */
|
|
$value = preg_replace( '#<([^>]+?)(href|src)=(\'|")<fileStore\.([\d\w\_]+?)>/#i', '<\1\2=\3%7BfileStore.\4%7D/', $value );
|
|
$value = preg_replace( '#<([^>]+?)(href|src)=(\'|")<___base_url___>/#i', '<\1\2=\3%7B___base_url___%7D/', $value );
|
|
$value = preg_replace( '#<([^>]+?)(data-fileid)=(\'|")<___base_url___>/#i', '<\1\2=\3%7B___base_url___%7D/', $value );
|
|
|
|
/* Start with legacy emoticon parsing */
|
|
$value = str_replace( "<#EMO_DIR#>", "<#EMO_DIR>", $value );
|
|
$value = preg_replace( "#(\s)?<([^>]+?)emoid=\"(.+?)\"([^>]*?)".">(\s)?#is", "\\1\\3\\5", $value );
|
|
|
|
preg_match_all( "#(<img(?:[^>]+?)class=['\"]bbc_emoticon[\"'](?:[^>]+?)alt=['\"](.+?)[\"'](?:[^>]+?)?>)#is", $value, $matches );
|
|
|
|
if( is_array($matches[1]) AND count($matches[1]) )
|
|
{
|
|
foreach( $matches[1] as $index => $match )
|
|
{
|
|
$value = str_replace( $match, $matches[2][ $index ], $value );
|
|
}
|
|
}
|
|
|
|
/* Parse emoticons */
|
|
usort( $this->emoticons, function( $a, $b ){
|
|
if ( mb_strlen( $a['typed'] ) == mb_strlen( $b['typed'] ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return ( mb_strlen( $a['typed'] ) > mb_strlen( $b['typed'] ) ) ? -1 : 1;
|
|
});
|
|
|
|
foreach( $this->emoticons as $emoticon )
|
|
{
|
|
$code = str_replace( '<', '<', str_replace( '>', '>', $emoticon['typed'] ) );
|
|
|
|
if( !$code or !\stristr( $value, $code ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( ! \IPS\File::isFullyQualifiedUrl( $emoticon['image'] ) )
|
|
{
|
|
$emoticon['image'] = \IPS\File::getClass('core_Emoticons')->baseUrl() . '/' . $emoticon['image'];
|
|
}
|
|
|
|
$quotedCode = preg_quote( $code, '/' );
|
|
$value = preg_replace( "/(^|[^a-zA-Z0-9\'\"\/]){$quotedCode}($|[^a-zA-Z0-9\'\"\/])/i", "$1<img src='" . $emoticon['image'] . "' alt='" . $code . "'>$2", $value );
|
|
}
|
|
|
|
/* Old char conversions */
|
|
$value = str_replace( " ", " ", $value );
|
|
$value = str_replace( "'", "'", $value );
|
|
$value = str_replace( ":", ":", $value );
|
|
$value = str_replace( "&", "&", $value );
|
|
/* The macro is not replaced out at runtime in 4.x and if we've made it to this point it is because the direct URL is still present
|
|
and the emoticon was not properly embedded. When trying to parse a URL with this macro in it, an error is thrown and the image will
|
|
always be broken, so our best option is to swap the macro out with the default folder ("default") as this will work for most users
|
|
and there is no other option to look up the emoticon at this point */
|
|
$value = str_replace( "<#EMO_DIR>", "default", $value );
|
|
|
|
/* Unconvert code */
|
|
$value = preg_replace_callback( "#<!--sql-->(.+?)<!--sql1-->(.+?)<!--sql2-->(.+?)<!--sql3-->#is", array( $this, '_parseOldCode'), $value );
|
|
$value = preg_replace_callback( "#<!--html-->(.+?)<!--html1-->(.+?)<!--html2-->(.+?)<!--html3-->#is", array( $this, '_parseOldCode'), $value );
|
|
$value = preg_replace_callback( "#<!--Flash (.+?)-->.+?<!--End Flash-->#", array( $this, '_parseOldFlash'), $value );
|
|
$value = preg_replace( "#<!--c1-->(.+?)<!--ec1-->#", '[code]', $value );
|
|
$value = preg_replace( "#<!--c2-->(.+?)<!--ec2-->#", '[/code]', $value );
|
|
$value = preg_replace( "#<div class=[\"']codetop['\"]>(.+?)</div><div class=[\"']codemain['\"] style=[\"']height:200px;white\-space:pre;overflow:auto['\"]>(.+?)</div>#is", "[code]\\2[/code]", $value );
|
|
|
|
/* Capital inconsistency */
|
|
foreach( array_keys( $this->parser->bbcodeTags( $this->member, TRUE ) ) as $bbcode )
|
|
{
|
|
$value = str_replace( '[' . mb_strtoupper( $bbcode ), '[' . $bbcode, $value );
|
|
$value = str_replace( '[/' . mb_strtoupper( $bbcode ), '[/' . $bbcode, $value );
|
|
}
|
|
|
|
/* Preserve code data */
|
|
$codeboxes = array();
|
|
preg_match_all( "/\[(code|codebox|sql|php|xml|html)(.*?)\](.+?)\[\/(code|codebox|sql|php|xml|html)\]/ims", $value, $matches );
|
|
|
|
foreach( $matches[0] as $k => $m )
|
|
{
|
|
$c = count( $codeboxes );
|
|
$codeboxes[ $c ] = $m;
|
|
|
|
$replacement = '<!--codeboxes{' . $c . '}-->';
|
|
|
|
$value = str_replace( $m, $replacement, $value );
|
|
}
|
|
|
|
preg_match_all( "/\<pre\s+?class=[\"']_prettyXprint(?:.*?)[\"']\>(.+?)\<\/pre\>/ims", $value, $matches );
|
|
|
|
foreach( $matches[0] as $k => $m )
|
|
{
|
|
$c = count( $codeboxes );
|
|
$codeboxes[ $c ] = $m;
|
|
|
|
$replacement = '<!--codeboxes{' . $c . '}-->';
|
|
|
|
$value = str_replace( $m, $replacement, $value );
|
|
}
|
|
|
|
/* Nested spoilers do not parse correctly, so just do that manually here for now */
|
|
$value = str_replace( "[spoiler]", '</p><div class="ipsSpoiler" data-ipsSpoiler><div class="ipsSpoiler_header"><span></span></div><div class="ipsSpoiler_contents"><p>', $value );
|
|
$value = str_replace( "[/spoiler]", "</p></div></div><p>", $value );
|
|
|
|
/* Just remove this - our new parser doesn't need (or like) it */
|
|
$value = str_replace( "[/*]", '', $value );
|
|
|
|
/* URL bbcode tags that didn't have a scheme specified would get fixed automatically in 3.x */
|
|
$value = preg_replace_callback( "/\[url\](.+?)\[\/url\]/i", function( $matches ){
|
|
if( mb_substr( $matches[1], 0, 7 ) !== 'http://' AND mb_substr( $matches[1], 0, 8 ) !== 'https://' AND mb_substr( $matches[1], 0, 6 ) !== 'ftp://' )
|
|
{
|
|
return 'http://' . $matches[1];
|
|
}
|
|
else
|
|
{
|
|
return $matches[1];
|
|
}
|
|
}, $value );
|
|
|
|
/* Convert some old HTML back into bbcode to be properly parsed */
|
|
$value = preg_replace( "#<a href=[\"']index\.php\?automodule=blog(&|&)showentry=(.+?)['\"]>(.+?)</a>#is", "[entry=\"\\2\"]\\3[/entry]", $value );
|
|
$value = preg_replace( "#<a href=[\"']index\.php\?automodule=blog(&|&)blogid=(.+?)['\"]>(.+?)</a>#is", "[blog=\"\\2\"]\\3[/blog]", $value );
|
|
$value = preg_replace( "#<a href=[\"']index\.php\?act=findpost(&|&)pid=(.+?)['\"]>(.+?)</a>#is", "[post=\"\\2\"]\\3[/post]", $value );
|
|
$value = preg_replace( "#<a href=[\"']index\.php\?showtopic=(.+?)['\"]>(.+?)</a>#is", "[topic=\"\\1\"]\\2[/topic]", $value );
|
|
$value = preg_replace( "#<a href=[\"'](.*?)index\.php\?act=findpost(&|&)pid=(.+?)['\"]><\{POST_SNAPBACK\}></a>#is", "[snapback]\\3[/snapback]", $value );
|
|
$value = preg_replace( "#<!--blog\.extract\.start-->(.+?)<!--blog\.extract\.end-->#is", "[extract]\\1[/extract]", $value );
|
|
$value = preg_replace( "#<span style=[\"']color:\#000000;background:\#000000['\"]>(.+?)</span>#is", "[spoiler]\\1[/spoiler]", $value );
|
|
|
|
/* Unconvert quotes */
|
|
$value = preg_replace( "#<!--QuoteBegin-->(.+?)<!--QuoteEBegin-->#" , '[quote]' , $value );
|
|
$value = preg_replace( "#<!--QuoteBegin-{1,2}([^>]+?)\+([^>]+?)-->(.+?)<!--QuoteEBegin-->#" , "[quote name='\\1' date='\\2']" , $value );
|
|
$value = preg_replace( "#<!--QuoteBegin-{1,2}([^>]+?)\+-->(.+?)<!--QuoteEBegin-->#" , "[quote name='\\1']" , $value );
|
|
$value = preg_replace( "#<!--QuoteEnd-->(.+?)<!--QuoteEEnd-->#" , '[/quote]' , $value );
|
|
$value = preg_replace( "#\[quote=(.+?),(.+?)\]#i" , "[quote name='\\1' date='\\2']" , $value );
|
|
$value = preg_replace( "#\[quote=(.*?)\[url(.*?)\](.+?)\[\/url\]\]#i" , "[quote=\\1\\3]" , $value );
|
|
$value = preg_replace_callback( "#<!--quoteo([^>]+?)?-->(.+?)<!--quotec-->#si" , array( $this, '_parseOldQuote' ) , $value );
|
|
|
|
/* Unconvert indent tag */
|
|
while( preg_match( "#<blockquote>(.+?)</blockquote>#is", $value ) )
|
|
{
|
|
$value = preg_replace( "#<blockquote>(.+?)</blockquote>#is" , "[indent]\\1[/indent]", $value );
|
|
}
|
|
|
|
/* Convert quote tag */
|
|
$value = preg_replace_callback( "#<blockquote\s+?class=['\"]ipsBlockquote[\"']([^>]*?)>#si", array( $this, '_parseOldBlockquote' ) , $value );
|
|
$value = preg_replace_callback( "#\[quote([^>]*?)\]#si" , array( $this, '_parseOldQuoteBbcode' ) , $value );
|
|
$value = str_replace( "</blockquote>", "</div></blockquote>", $value );
|
|
$value = str_replace( "[/quote]", "</div></blockquote>", $value );
|
|
|
|
/* Fix download manager embedded screenshot URLs */
|
|
$value = preg_replace_callback( "#<img src=['\"]" . preg_quote( rtrim( \IPS\Settings::i()->base_url, '/' ), '#' ) . "/index\.php\?app=downloads(?:&|&)module=display(?:&|&)section=screenshot(?:&|&)id=(\d+?)['\"]([^>]*?)>#si", array( $this, '_fixDownloadsScreenshots' ), $value );
|
|
|
|
/* These were auto-replacements previously */
|
|
$value = str_ireplace( "(c)" , "©" , $value );
|
|
$value = str_ireplace( "(tm)" , "™" , $value );
|
|
$value = str_ireplace( "(r)" , "®" , $value );
|
|
|
|
/* Convert PHP tags if necessary */
|
|
$value = str_ireplace( "<?php" , "<?php", $value );
|
|
$value = str_ireplace( "?>" , "?>" , $value );
|
|
|
|
/* Fix bbcode attributes */
|
|
preg_match_all( "/\[([^\]]+?)=(\"|'|'|'|")([^\]]+?)(\"|'|'|'|")\]/", $value, $matches );
|
|
|
|
foreach( $matches[0] as $bbcodeTag )
|
|
{
|
|
$newBbcodeTag = preg_replace_callback( "/\[([^\]]+?)=(\"|'|'|'|")([^\]]+?)(\"|'|'|'|")\]/", function( $tagMatches ) {
|
|
/* We strip the enclosing quotes and we strip any trailing ';' */
|
|
return "[" . $tagMatches[1] . "=" . rtrim( $tagMatches[3], ';' ) . "]";
|
|
}, $bbcodeTag );
|
|
|
|
$value = str_replace( $bbcodeTag, $newBbcodeTag, $value );
|
|
}
|
|
|
|
/* Convert bbcode tags, but only those our new parser doesn't handle */
|
|
foreach( $this->bbcodes as $code )
|
|
{
|
|
/* If we don't have a regex replacement, probably because we used to use a plugin, we can't do this automatically */
|
|
if( !$code['bbcode_replace'] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Build the regex */
|
|
$regex = "/\[(?:{$code['bbcode_tag']}" . ( $code['bbcode_aliases'] ? '|' . str_replace( ',', '|', $code['bbcode_aliases'] ) : '' ) . ")" .
|
|
( $code['bbcode_useoption'] ? ( $code['bbcode_optional_option'] ? "(?:=(.+?))?" : "=(.+?)" ) : '' ) . "\]" .
|
|
( $code['bbcode_single_tag'] ? '' : ( "(.+?)\[\/(?:{$code['bbcode_tag']}" . ( $code['bbcode_aliases'] ? '|' . str_replace( ',', '|', $code['bbcode_aliases'] ) : '' ) . ")\]" ) ) .
|
|
"/ims";
|
|
|
|
/* Now actually perform the replacement */
|
|
$depth = 0;
|
|
while( preg_match( $regex, $value ) )
|
|
{
|
|
/* We want to allow nested BBCodes, but we don't want an infinite loop */
|
|
$depth++;
|
|
if ( $depth > 20 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
$value = preg_replace_callback( $regex, function( $matches ) use ( $code ) {
|
|
$replacement = $code['bbcode_replace'];
|
|
|
|
if( $code['bbcode_single_tag'] )
|
|
{
|
|
if( $code['bbcode_useoption'] )
|
|
{
|
|
$replacement = str_replace( '{option}', $matches[1], $replacement );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( $code['bbcode_useoption'] )
|
|
{
|
|
$replacement = str_replace( '{option}', $matches[1], $replacement );
|
|
$replacement = str_replace( '{content}', $matches[2], $replacement );
|
|
}
|
|
else
|
|
{
|
|
$replacement = str_replace( '{content}', $matches[1], $replacement );
|
|
}
|
|
}
|
|
|
|
return $replacement;
|
|
}, $value );
|
|
}
|
|
}
|
|
|
|
/* Figure out what attachments are missing so we can add them at the end */
|
|
if( $this->attachClass !== NULL AND $this->idOne !== 0 )
|
|
{
|
|
$embeddedAttachments = array();
|
|
|
|
preg_match_all( "/\[attachment=(.+?):(.+?)\]/ims", $value, $matches );
|
|
|
|
if( count( $matches[1] ) )
|
|
{
|
|
foreach( $matches[1] as $id )
|
|
{
|
|
$embeddedAttachments[ $id ] = $id;
|
|
}
|
|
}
|
|
|
|
$where = array( array( 'location_key=?', $this->attachClass ), array( 'id1=?', $this->idOne ) );
|
|
|
|
if( $this->idTwo !== 0 )
|
|
{
|
|
$where[] = array( 'id2=?', $this->idTwo );
|
|
}
|
|
|
|
$mappedAttachments = iterator_to_array( \IPS\Db::i()->select( '*', 'core_attachments_map', $where )->setKeyField( 'attachment_id' ) );
|
|
|
|
foreach( $mappedAttachments as $attachmentId => $map )
|
|
{
|
|
if( !in_array( $attachmentId, $embeddedAttachments ) )
|
|
{
|
|
$value .= '<p>[attachment=' . $attachmentId . ':name]</p>';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now fix shared media */
|
|
$value = preg_replace_callback( "/\[sharedmedia=(.+?):(.+?):(.+?)\]/ims", function( $matches ) {
|
|
|
|
switch( $matches[1] )
|
|
{
|
|
case 'calendar':
|
|
try
|
|
{
|
|
$event = \IPS\Db::i()->select( '*', 'calendar_events', array( 'event_id=?', (int) $matches[3] ) )->first();
|
|
$url = \IPS\Http\Url::internal( "app=calendar&module=events&controller=event&id={$event['event_id']}", 'front', 'calendar_event', $event['event_title_seo'] );
|
|
return "<p><a href='{$url}'>{$url}</a></p>";
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return "";
|
|
}
|
|
break;
|
|
|
|
case 'gallery':
|
|
/* Make sure gallery is enabled first */
|
|
if ( \IPS\Application::appIsEnabled( 'gallery' ) === FALSE )
|
|
{
|
|
return "";
|
|
}
|
|
|
|
if( $matches[2] == 'images' )
|
|
{
|
|
try
|
|
{
|
|
$image = \IPS\gallery\Image::constructFromData( \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_id=?', (int) $matches[3] ) )->first() );
|
|
return "<p><a href='{$image->url()}'><img src='{$image->embedImage()->url}' alt='{$image->caption}'></a></p>";
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
$album = \IPS\gallery\Album::constructFromData( \IPS\Db::i()->select( '*', 'gallery_albums', array( 'album_id=?', (int) $matches[3] ) )->first() );
|
|
return \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $album->url(), TRUE, TRUE ) );
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'downloads':
|
|
try
|
|
{
|
|
$file = \IPS\Db::i()->select( '*', 'downloads_files', array( 'file_id=?', (int) $matches[3] ) )->first();
|
|
$url = \IPS\Http\Url::internal( "app=downloads&module=downloads&controller=view&id={$file['file_id']}", 'front', 'downloads_file', $file['file_name_furl'] );
|
|
return "<p><a href='{$url}'>{$url}</a></p>";
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return "";
|
|
}
|
|
break;
|
|
|
|
case 'core':
|
|
/* We'll just let the attachment parsing next take care of this */
|
|
return "[attachment={$matches[3]}:string]";
|
|
break;
|
|
|
|
case 'blog':
|
|
try
|
|
{
|
|
$entry = \IPS\Db::i()->select( '*', 'blog_entries', array( 'entry_id=?', (int) $matches[3] ) )->first();
|
|
$url = \IPS\Http\Url::internal( "app=blog&module=blogs&controller=entry&id={$entry['entry_id']}", 'front', 'blog_entry', $entry['entry_name_seo'] );
|
|
return "<p><a href='{$url}'>{$url}</a></p>";
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return "";
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Could be a third party app - just return original embed code if we are still here */
|
|
return $matches[0];
|
|
}, $value );
|
|
|
|
/* Some people may have tried to embed the direct youtube video...tsk tsk */
|
|
preg_match_all( '#youtube.com/v/([^ \[]+?)#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $idx => $m )
|
|
{
|
|
$value = str_replace( $m, "youtube.com/watch?v=" . $matches[1][ $idx ], $value );
|
|
}
|
|
|
|
/* We previously supported multiple types of 'media' tags which we need to convert now */
|
|
foreach( array( 'youtube', 'blogmedia', 'flash', 'movie', 'video' ) as $tagname )
|
|
{
|
|
$value = str_replace( array( '[' . $tagname . ']', '[/' . $tagname . ']' ), array( '', ' ' ), $value );
|
|
}
|
|
|
|
/* Media */
|
|
$urls = array();
|
|
|
|
/* Parse [URL]url[/URL] bbcode */
|
|
preg_match_all( '#\[url]([^\[]+?)\[\/url\]#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $idx => $m )
|
|
{
|
|
$value = str_replace( $m, "<a href='" . $matches[1][ $idx ] . "'>" . $matches[1][ $idx ] . "</a>", $value );
|
|
}
|
|
|
|
/* Parse [URL=url]text[/URL] bbcode */
|
|
preg_match_all( '#\[url=(?:\'|"|&\#039;)?(.+?)(?:\'|"|&\#039;)?\](.+?)\[\/url\]#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $idx => $m )
|
|
{
|
|
$value = str_replace( $m, "<a href='" . $matches[1][ $idx ] . "'>" . $matches[2][ $idx ] . "</a>", $value );
|
|
}
|
|
|
|
/* Parse img bbcode */
|
|
preg_match_all( '#\[img\](.+?)\[\/img\]#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $idx => $m )
|
|
{
|
|
$image = basename( $matches[1][ $idx ] );
|
|
$value = str_replace( $m, "<img src='" . $matches[1][ $idx ] . "' alt='{$image}'>", $value );
|
|
}
|
|
|
|
/* Get rid of empty hyperlinks, which can be present from bugs in older versions of IPB */
|
|
preg_match_all( '#<a(?:[^>]+?)href=[\'"]["\'](?:[^>]*?)>(.+?)\<\/a\>#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $k => $v )
|
|
{
|
|
$value = str_replace( $v, $matches[1][ $k ], $value );
|
|
}
|
|
|
|
/* Get rid of nested iframes if they exist */
|
|
preg_match_all( "/<iframe src=['\"]<iframe(.+?)>[\"'].+?>/", $value, $matches );
|
|
|
|
foreach( $matches[1] as $k => $m )
|
|
{
|
|
$realUrl = preg_replace( "/^.*?src=['\"]([^'\"]+?)[\"'].*?$/", "$1", $m );
|
|
|
|
$value = str_replace( $matches[0][ $k ], $realUrl, $value );
|
|
}
|
|
|
|
/* Fix hrefs missing the wrapping " or ', but first protect <fileStore...> */
|
|
$value = preg_replace( '#<([^>]+?)(href|src)=(\'|")<fileStore\.([\d\w\_]+?)>/#i', '<\1\2=\3%7BfileStore.\4%7D/', $value );
|
|
|
|
$value = preg_replace_callback( '#<a (.+?)>#is', function( $matches ){
|
|
if( mb_strpos( $matches[1], 'href="' ) !== FALSE OR mb_strpos( $matches[1], "href='" ) !== FALSE )
|
|
{
|
|
return $matches[0];
|
|
}
|
|
|
|
$params = explode( ' ', $matches[1] );
|
|
|
|
foreach( $params as $_idx => $attribute )
|
|
{
|
|
$attribute = trim( $attribute );
|
|
|
|
if( mb_strpos( $attribute, 'href=' ) === 0 )
|
|
{
|
|
$params[ $_idx ] = "href='" . str_replace( 'href=', '', $attribute ) . "'";
|
|
break;
|
|
}
|
|
}
|
|
|
|
return "<a " . implode( ' ', $params ) . '>';
|
|
}, $value );
|
|
|
|
$value = preg_replace( '#<([^>]+?)(href|src)=(\'|")%7BfileStore\.([\d\w\_]+?)%7D/#i', '<\1\2=\3<fileStore.\4>/', $value );
|
|
|
|
/* Store links */
|
|
preg_match_all( '#<a.+?href=[\'"](.*?)["\'].*?>(.+?)\<\/a\>#is', $value, $matches );
|
|
|
|
/* Embeds */
|
|
$embedCount = 0;
|
|
|
|
foreach( $matches[0] as $k => $m )
|
|
{
|
|
$url = $matches[1][ $k ];
|
|
|
|
if( mb_strpos( $url, '#entry' ) !== FALSE )
|
|
{
|
|
$sep = '?';
|
|
|
|
if( mb_strpos( $url, '?' ) )
|
|
{
|
|
$sep = '&';
|
|
}
|
|
|
|
$url = preg_replace( "/#entry(\d+)/", $sep . "do=findComment&comment=$1", $url );
|
|
}
|
|
|
|
$c = count( $urls );
|
|
$urls[ $c ] = str_replace( $matches[1][ $k ], $url, $m );
|
|
|
|
$replacement = '<!--url{' . $c . '}-->';
|
|
|
|
try
|
|
{
|
|
/* In 3.x, hyperlinked YouTube URLs did replace as long as a data attribute wasn't present */
|
|
if( ! mb_strpos( $matches[1][ $k ], \IPS\Settings::i()->base_url ) AND ( mb_strpos( $matches[0][ $k ], 'youtube.co' ) OR mb_strpos( $matches[0][ $k ], 'youtu.be' ) ) and ! mb_strpos( $matches[0][ $k ], 'nomediaparse' ) AND $response = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $matches[1][ $k ], FALSE, TRUE ) ) )
|
|
{
|
|
$urls[ $c ] = $response;
|
|
}
|
|
}
|
|
catch( \UnexpectedValueException $e ){}
|
|
|
|
$value = str_replace( $m, $replacement, $value );
|
|
}
|
|
|
|
/* Store images */
|
|
preg_match_all( '#<img[^>]+?src=[\'"](.+?)["\'].*?>#is', $value, $matches );
|
|
|
|
foreach( $matches[0] as $m )
|
|
{
|
|
$c = count( $urls );
|
|
$urls[ $c ] = $m;
|
|
|
|
$value = str_replace( $m, '<!--url{' . $c . '}-->', $value );
|
|
}
|
|
|
|
/* Media URLs such as [media]http://site.com/something (data).mp3[/media] will not match the next regex, so fix them first */
|
|
preg_match_all( "/\[media\](.+?)\[\/media\]/is", $value, $matches );
|
|
|
|
foreach( $matches[1] as $k => $m )
|
|
{
|
|
$c = count( $urls );
|
|
$urls[ $c ] = "<a href='{$m}'>{$m}</a>";
|
|
|
|
$value = str_replace( $matches[0][ $k ], '<!--url{' . $c . '}-->', $value );
|
|
}
|
|
|
|
/* Parse standard URLs */
|
|
preg_match_all( '#(?:^|\s|\)|\(|\{|\}|/>|>|\]|\[|;|href=\S)((http|https|news|ftp)://(?:[^<>\)\[\"\s]+|[a-zA-Z0-9/\._\-!&\#;,%\+\?:=]+))#is', $value, $matches );
|
|
|
|
foreach( $matches[1] as $k => $m )
|
|
{
|
|
$me = null;
|
|
|
|
/* If this was an image URL that was not already embedded, we shouldn't try to embed it now - also this causes timeouts in
|
|
many cases as the images that were linked in old posts are no longer available. Also don't try to embed 50 youtube videos in one post. */
|
|
if( !preg_match( "/(.+?)\.(jpg|jpeg|png|gif)$/i", $m ) AND $embedCount < $this->maxEmbeds )
|
|
{
|
|
$url = $m;
|
|
|
|
if( mb_strpos( $m, '#entry' ) !== FALSE )
|
|
{
|
|
$sep = '?';
|
|
|
|
if( mb_strpos( $m, '?' ) )
|
|
{
|
|
$sep = '&';
|
|
}
|
|
|
|
$url = preg_replace( "/#entry(\d+)/", $sep . "do=findComment&comment=$1", $m );
|
|
}
|
|
|
|
try
|
|
{
|
|
$me = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $url, FALSE, TRUE ) );
|
|
}
|
|
catch( \Exception $e ){}
|
|
}
|
|
|
|
if( $me )
|
|
{
|
|
$embedCount++;
|
|
}
|
|
|
|
$c = count( $urls );
|
|
$urls[ $c ] = $me ?: "<a href='{$m}'>{$m}</a>";
|
|
|
|
$value = str_replace( $matches[1][ $k ], '<!--url{' . $c . '}-->', $value );
|
|
}
|
|
|
|
foreach( $urls as $k => $v )
|
|
{
|
|
$value = str_replace( '<!--url{' . $k . '}-->', $v, $value );
|
|
}
|
|
|
|
$value = preg_replace( "/\<a href=([^>]+?)\>\<a href=([^>]+?)\>(.+?)\<\/a\>\<\/a\>/ims", "<a href=$1>$3</a>", $value );
|
|
|
|
/* Now fix attachments */
|
|
$value = preg_replace_callback( "/\[attachment=(.+?):(.+?)\]/ims", function( $matches ) {
|
|
try
|
|
{
|
|
$attachment = \IPS\Db::i()->select( '*', 'core_attachments', array( 'attach_id=?', (int) $matches[1] ) )->first();
|
|
}
|
|
catch( \UnderflowException $e )
|
|
{
|
|
$attachment = array( 'attach_is_image' => 0, 'attach_location' => '' );
|
|
}
|
|
|
|
if( $attachment['attach_is_image'] )
|
|
{
|
|
$attachment['attach_thumb_location'] = $attachment['attach_thumb_location'] ?: $attachment['attach_location'];
|
|
$return = "<a class='ipsAttachLink ipsAttachLink_image' href='{fileStore.core_Attachment}/{$attachment['attach_location']}'><img src='{fileStore.core_Attachment}/{$attachment['attach_thumb_location']}' data-fileid='" . \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php?id={$attachment['attach_id']}' class='ipsImage ipsImage_thumbnailed'></a>";
|
|
}
|
|
else
|
|
{
|
|
$return = $attachment['attach_location'] ? "<a href='" . \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php?id={$attachment['attach_id']}'>{$attachment['attach_file']}</a>" : '';
|
|
}
|
|
|
|
return $return;
|
|
}, $value );
|
|
|
|
/* Some old posts might have used <br> instead of <p>, which can cause severe parsing issues, so try to catch this and adapt */
|
|
if( mb_substr( $value, 0, 3 ) !== '<p>' AND mb_substr( $value, 0, 3 ) !== '<p ' AND mb_substr( $value, 0, 5 ) !== '<div>' AND mb_substr( $value, 0, 5 ) !== '<div ' AND ( mb_strpos( $value, '<br>' ) !== FALSE OR mb_strpos( $value, '<br />' ) !== FALSE ) )
|
|
{
|
|
$value = '<p>' . $value . '</p>';
|
|
$value = str_replace( array( '<br>', '<br />' ), '</p><p>', $value );
|
|
}
|
|
|
|
$value = preg_replace( '/<p>\s*<\/p>/i', '<p> </p>', $value );
|
|
|
|
/* But that may break blockquotes, which are block elements */
|
|
$value = str_replace( "<p><blockquote", "<blockquote", $value );
|
|
$value = str_replace( "</blockquote></p>", "</blockquote>", $value );
|
|
|
|
/* Fix lists */
|
|
$value = str_replace( "[list]", "</p><ul data-ipsBBCode-list=\"true\">", $value );
|
|
|
|
$value = preg_replace_callback( "/\[list=['\"]?(.+?)['\"]?\](.+?)\[\/list\]/ims", function( $matches ){
|
|
switch( $matches[1] )
|
|
{
|
|
case '1':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: decimal'>{$matches[2]}</ol><p>";
|
|
break;
|
|
|
|
case '0':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: decimal-leading-zero'>{$matches[2]}</ol><p>";
|
|
break;
|
|
|
|
case 'a':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: lower-alpha'>{$matches[2]}</ol><p>";
|
|
break;
|
|
|
|
case 'A':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: upper-alpha'>{$matches[2]}</ol><p>";
|
|
break;
|
|
|
|
case 'i':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: lower-roman'>{$matches[2]}</ol><p>";
|
|
break;
|
|
|
|
case 'I':
|
|
return "</p><ol data-ipsBBCode-list=\"true\" style='list-style-type: upper-roman'>{$matches[2]}</ol><p>";
|
|
break;
|
|
}
|
|
|
|
return "</p><ul data-ipsBBCode-list=\"true\">{$matches[2]}</ul>";
|
|
}, $value );
|
|
|
|
$value = str_replace( "[/list]", "</ul><p>", $value );
|
|
|
|
$value = preg_replace( "/(<[u|o]l data-ipsBBCode-list=\"true\"(?:.+?)?>)\s*?<br \/>\s*?\[\*\]/", "$1\n[*]", $value );
|
|
$value = preg_replace( "/(<[u|o]l data-ipsBBCode-list=\"true\"(?:.+?)?>)\s*?<br>\s*?\[\*\]/", "$1\n[*]", $value );
|
|
$value = preg_replace( "/(<br \/>|<br>)\s*?\[\*\]/", "\n[*]", $value );
|
|
|
|
$value = preg_replace_callback( "/<[u|o]l data-ipsBBCode-list=\"true\"(?:.*?)>.+?<\/[u|o]l>/ims", function( $matches )
|
|
{
|
|
$v = str_replace( '</p><p>', '<br>', $matches[0] );
|
|
$v = str_replace( array( '<p>', '</p>' ), '', $v );
|
|
$v = str_replace( '[*]', '</li><li>', $v );
|
|
$v = str_replace( '<ul></li>', '<ul>', $v );
|
|
$v = str_replace( '<ol></li>', '<ol>', $v );
|
|
$v = str_replace( '</ul>', '</li></ul>', $v );
|
|
$v = str_replace( '</ol>', '</li></ol>', $v );
|
|
$v = str_replace( ' data-ipsBBCode-list="true"', '', $v );
|
|
return $v;
|
|
}, $value );
|
|
|
|
$value = preg_replace( "/(\<[u|o]l(?:.*?)?\>)\s*?\<\/li\>/ims", "$1", $value );
|
|
$value = preg_replace( "/(\<[u|o]l(?:.*?)?\>)\<br\>\<li\>/ims", "$1<li>", $value );
|
|
|
|
/* Put codeboxes back */
|
|
foreach( $codeboxes as $k => $v )
|
|
{
|
|
$value = str_replace( '<!--codeboxes{' . $k . '}-->', $v, $value );
|
|
}
|
|
|
|
/* Block level tags */
|
|
$value = preg_replace( "/\[(code|codebox|sql|php|xml|html)(.*?)\](.+?)\[\/(code|codebox|sql|php|xml|html)\]/ims", "</p><div>[$1$2]$3[/$4]</div><p>", $value );
|
|
$value = preg_replace( "/\[(indent|left|center|right|spoiler|page)\](.+?)\[\/(indent|left|center|right|spoiler|page)\]/ims", "</p><p>[$1]$2[/$3]</p><p>", $value );
|
|
$value = preg_replace( "/\<p\>\s*?\[page\]\s*?\<\/p\>/ims", "[page]", $value );
|
|
|
|
/* Fix old code class names */
|
|
$value = preg_replace_callback( "/<pre\s+?class=['\"](.+?)[\"']>/i", function( $matches ) {
|
|
$classes = explode( ' ', $matches[1] );
|
|
$newClasses = array();
|
|
|
|
foreach( $classes as $class )
|
|
{
|
|
if( $class == '_prettyXprint' )
|
|
{
|
|
$class = 'prettyprint';
|
|
}
|
|
|
|
if( mb_substr( $class, 0, 1 ) == '_' )
|
|
{
|
|
$class = mb_substr( $class, 1 );
|
|
}
|
|
|
|
$newClasses[] = $class;
|
|
}
|
|
|
|
return "<pre class='ipsCode " . implode( ' ', $newClasses ) . "'>";
|
|
}, $value );
|
|
|
|
/* Put the dynamic replacements back now */
|
|
$value = preg_replace( '#<([^>]+?)(href|src|srcset)=(\'|")(?:{|%7B)fileStore\.([\d\w\_]+?)(?:}|%7D)/#i', '<\1\2=\3<fileStore.\4>/', $value );
|
|
$value = preg_replace( '#<([^>]+?)(srcset)=(\'|")(?:{|%7B)fileStore\.([\d\w\_]+?)(?:}|%7D)/#i', '<\1\2=\3<fileStore.\4>/', $value );
|
|
$value = preg_replace( '#<([^>]+?)(href|src|srcset)=(\'|")(?:{|%7B)___base_url___(?:}|%7D)/#i', '<\1\2=\3<___base_url___>/', $value );
|
|
$value = preg_replace( '#<([^>]+?)(data-fileid)=(\'|")(?:{|%7B)___base_url___(?:}|%7D)/#i', '<\1\2=\3<___base_url___>/', $value );
|
|
|
|
//print htmlspecialchars( $value );print "<hr style='color:red;'>";print $value;print "<hr style='color:red;'>";
|
|
|
|
/* Return */
|
|
if ( mb_stristr( $value, '<fileStore.' ) )
|
|
{
|
|
\IPS\Output::i()->parseFileObjectUrls( $value );
|
|
}
|
|
|
|
$result = $this->parser->parse( $value );
|
|
|
|
/* If they are not using UTF8MB4, we need to convert emoji back to html entities as HTMLPurifier will have converted them on us */
|
|
if ( \IPS\Settings::i()->getFromConfGlobal('sql_utf8mb4') !== TRUE )
|
|
{
|
|
$result = preg_replace_callback( '/[\x{10000}-\x{10FFFF}]/u', function( $mb4Character ) {
|
|
return mb_convert_encoding( $mb4Character[0], 'HTML-ENTITIES', 'UTF-8' );
|
|
}, $result );
|
|
}
|
|
|
|
/* URLs that are turned into embeds end up as <a href=''><iframe ...></a> */
|
|
$result = preg_replace( "/\<a href=[\"']{2}.*?\>(?:>)?(\<iframe.+?\>)\<\/a\>/i", "$1", $result );
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Parse new quotes
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _parseOldQuote( $matches=array() )
|
|
{
|
|
if ( !$matches[1] )
|
|
{
|
|
return '[quote]';
|
|
}
|
|
else
|
|
{
|
|
$return = array();
|
|
|
|
preg_match( "#\(post=(.+?)?:date=(.+?)?:name=(.+?)?\)#", $matches[1], $match );
|
|
|
|
if ( $match[3] )
|
|
{
|
|
$return[] = " name='" . static::_cleanQuoteName( $match[3] ) . "'";
|
|
}
|
|
|
|
if ( $match[1] )
|
|
{
|
|
$return[] = " post='" . intval($match[1]) . "'";
|
|
}
|
|
|
|
if ( $match[2] )
|
|
{
|
|
$return[] = " date='{$match[2]}'";
|
|
}
|
|
|
|
return str_replace( ' ', ' ', '[quote' . implode( ' ', $return ).']' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse new quotes #2
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _parseOldBlockquote( $matches=array() )
|
|
{
|
|
$parameters = array( 'data-ipsQuote' => '', 'class' => 'ipsQuote' );
|
|
|
|
if( count( $matches[1] ) )
|
|
{
|
|
preg_match( "/data-author=['\"](.+?)[\"']/i", static::_cleanQuoteName( $matches[1] ), $author );
|
|
preg_match( "/data-cid=['\"](.+?)[\"']/i", $matches[1], $cid );
|
|
preg_match( "/data-time=['\"](.+?)[\"']/i", $matches[1], $time );
|
|
|
|
if( isset( $cid[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-contentcommentid'] = $cid[1];
|
|
|
|
if( $this->attachClass and $this->itemClass )
|
|
{
|
|
$pieces = explode( '_', $this->attachClass );
|
|
|
|
$parameters['data-ipsquote-contentapp'] = $pieces[0];
|
|
$parameters['data-ipsquote-contenttype'] = mb_strtolower( $pieces[1] );
|
|
$parameters['data-ipsquote-contentclass'] = str_replace( '\\', '_', mb_substr( $this->itemClass, 4 ) );
|
|
$parameters['data-ipsquote-contentid'] = $this->idOne;
|
|
}
|
|
}
|
|
|
|
if( isset( $author[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-username'] = $author[1];
|
|
$parameters['data-cite'] = $author[1];
|
|
}
|
|
|
|
if( isset( $time[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-timestamp'] = $time[1];
|
|
}
|
|
}
|
|
|
|
$_parameterString = '';
|
|
|
|
foreach( $parameters as $key => $value )
|
|
{
|
|
$_parameterString .= ' ' . $key . '="' . str_replace( '"', '\\"', $value ) . '"';
|
|
}
|
|
|
|
return "<blockquote{$_parameterString}><div>";
|
|
}
|
|
|
|
/**
|
|
* Parse new quotes #3
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _parseOldQuoteBbcode( $matches=array() )
|
|
{
|
|
$parameters = array( 'data-ipsQuote' => '', 'class' => 'ipsQuote' );
|
|
|
|
if( count( $matches[1] ) )
|
|
{
|
|
preg_match( "/name=['\"](.+?)[\"']/i", $matches[1], $author );
|
|
preg_match( "/post=['\"](.+?)[\"']/i", $matches[1], $cid );
|
|
preg_match( "/timestamp=['\"](.+?)[\"']/i", $matches[1], $time );
|
|
|
|
if( isset( $cid[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-contentcommentid'] = $cid[1];
|
|
}
|
|
|
|
if( isset( $author[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-username'] = $author[1];
|
|
$parameters['data-cite'] = $author[1];
|
|
}
|
|
|
|
if( isset( $time[1] ) )
|
|
{
|
|
$parameters['data-ipsquote-timestamp'] = $time[1];
|
|
}
|
|
}
|
|
|
|
/* Try to set the other parameters */
|
|
if( isset( $this->attachClass ) )
|
|
{
|
|
$attachBits = explode( '_', $this->attachClass );
|
|
|
|
$parameters['data-ipsquote-contentapp'] = $attachBits[0];
|
|
$parameters['data-ipsquote-contenttype'] = mb_strtolower( $attachBits[1] );
|
|
|
|
/* This is not perfect - if you quoted from another topic this would be wrong, however in most cases
|
|
this is the best guess so we'll use it */
|
|
$parameters['data-ipsquote-contentid'] = $this->idOne;
|
|
}
|
|
|
|
if( isset( $this->itemClass ) )
|
|
{
|
|
$parameters['data-ipsquote-contentclass'] = str_replace( '\\', '_', mb_substr( $this->itemClass, 4 ) );
|
|
}
|
|
|
|
$_parameterString = '';
|
|
|
|
foreach( $parameters as $key => $value )
|
|
{
|
|
$_parameterString .= ' ' . $key . '="' . str_replace( '"', '\\"', $value ) . '"';
|
|
}
|
|
|
|
return "<blockquote{$_parameterString}><div>";
|
|
}
|
|
|
|
/**
|
|
* Convert flash HTML back into BBCode
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _parseOldFlash( $matches=array() )
|
|
{
|
|
$f_arr = explode( "+", $matches[1] );
|
|
|
|
return '[flash=' . $f_arr[0] . ',' . $f_arr[1] . ']' . $f_arr[2] . '[/flash]';
|
|
}
|
|
|
|
/**
|
|
* Convert old code tags back into bbcode
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _parseOldCode( $matches=array() )
|
|
{
|
|
return '[code]' . rtrim( str_replace( "</span>", '', preg_replace( "#<span style='.+?'>#is", "", stripslashes( $matches[2] ) ) ) ) . '[/code]';
|
|
}
|
|
|
|
/**
|
|
* Convert download manager screenshot URLs
|
|
*
|
|
* @param array Data from preg_replace callback
|
|
* @return string Converted text
|
|
*/
|
|
protected function _fixDownloadsScreenshots( $matches )
|
|
{
|
|
if( isset( $matches[1] ) )
|
|
{
|
|
try
|
|
{
|
|
$screenshot = \IPS\Db::i()->select( 'record_location, record_realname', 'downloads_files_records', array( "record_file_id=? and record_type IN('sslink','ssupload')", (int) $matches[1] ), 'record_id ASC', array( 0, 1 ) )->first();
|
|
|
|
return "<img src='{$screenshot['record_location']}' alt='{$screenshot['record_realname']}'>";
|
|
}
|
|
catch( \Exception $e )
|
|
{
|
|
return $matches[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return $matches[0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Legacy names can have things like $ and ' which break new parser
|
|
*
|
|
* @param string Name
|
|
* @return string Converted name
|
|
*/
|
|
protected static function _cleanQuoteName( $name )
|
|
{
|
|
$name = str_replace( "'", ''', $name );
|
|
$name = str_replace( '$', '$', $name );
|
|
$name = str_replace( '[', '[', $name );
|
|
$name = str_replace( ']', ']', $name );
|
|
|
|
return $name;
|
|
}
|
|
} |