Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief DOM Parser
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 8 Feb 2017
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM Parser
|
||||
*/
|
||||
class _DOMParser
|
||||
{
|
||||
/**
|
||||
* @brief Callback to parse a DOMElement object
|
||||
*/
|
||||
protected $elementParser;
|
||||
|
||||
/**
|
||||
* @brief Callback to parse a DOMText object
|
||||
*/
|
||||
protected $textParser;
|
||||
|
||||
/**
|
||||
* Parse
|
||||
*
|
||||
* @param string $value Contents to parse
|
||||
* @param callback $elementParser Callback to parse a DOMElement object. Is expected to call $parent->appendChild( $element ) after doing any parsing and call $parser->_parseDomNodeList(). function ( \DOMElement $element, \DOMNode $parent, \IPS\Text\DOMParser $parser )
|
||||
* @param callback|NULL $textParser Callback to parse a DOMText object. Is expected to call $parent->appendChild( $element ) after doing any parsing. function ( \DOMText $textNode, \DOMNode $parent, \IPS\Text\DOMParser $parser )
|
||||
* @retrun string
|
||||
*/
|
||||
public static function parse( $value, $elementParser, $textParser = NULL )
|
||||
{
|
||||
return static::getDocumentBodyContents( ( new static( $elementParser, $textParser ) )->parseValueIntoDocument( $value ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $value
|
||||
* @param callback $elementParser Callback to parse a DOMElement object. Is expected to call $parent->appendChild( $element ) after doing any parsing and call $parser->_parseDomNodeList(). function ( \DOMElement $element, \DOMNode $parent, \IPS\Text\DOMParser $parser )
|
||||
* @param callback|NULL $textParser Callback to parse a DOMText object. Is expected to call $parent->appendChild( $element ) after doing any parsing. function ( \DOMText $textNode, \DOMNode $parent, \IPS\Text\DOMParser $parser )
|
||||
* @retrun string
|
||||
*/
|
||||
public function __construct( $elementParser, $textParser = NULL )
|
||||
{
|
||||
$this->elementParser = $elementParser;
|
||||
$this->textParser = $textParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Value into DOMDocument
|
||||
*
|
||||
* @param string $value Contents to parse
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function parseValueIntoDocument( $value )
|
||||
{
|
||||
/* Load the value into a DOMDocument */
|
||||
$source = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' );
|
||||
$source->loadHTML( \IPS\Xml\DOMDocument::wrapHtml( $value ) );
|
||||
|
||||
/* Create a new DOMDocument which we will move nodes into */
|
||||
$document = new \IPS\Xml\DOMDocument( '1.0', 'UTF-8' );
|
||||
|
||||
/* Parse */
|
||||
$this->_parseDomNode( $source, $document );
|
||||
|
||||
/* Return */
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse DOMNode
|
||||
*
|
||||
* @param \DOMNode $node The node from the source document to parse
|
||||
* @param \DOMNode $parent The node from the new document which will be this node's parent
|
||||
* @return void
|
||||
*/
|
||||
public function _parseDomNode( \DOMNode $node, \DOMNode &$parent )
|
||||
{
|
||||
switch ( $node->nodeType )
|
||||
{
|
||||
/* This is the main DOMDocument object and it contains HTML. We just need to loop children */
|
||||
case XML_HTML_DOCUMENT_NODE:
|
||||
$this->_parseDomNodeList( $node->childNodes, $parent );
|
||||
break;
|
||||
|
||||
/* This is a HTML element (e.g. <html>, <p>, <a>, etc.) represented as a DOMElement object. Parse it. */
|
||||
case XML_ELEMENT_NODE:
|
||||
call_user_func( $this->elementParser, $node, $parent, $this );
|
||||
break;
|
||||
|
||||
/* This is text represented as a DOMText object. Parse it. */
|
||||
case XML_TEXT_NODE:
|
||||
if ( $this->textParser )
|
||||
{
|
||||
$function = $this->textParser;
|
||||
$function( $node, $parent, $this );
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent->appendChild( $parent->ownerDocument->importNode( $node ) );
|
||||
}
|
||||
break;
|
||||
|
||||
/* This is text represented as a DOMCharacterData object, for example, the
|
||||
contents of a <script> tag - we just insert it */
|
||||
case XML_CDATA_SECTION_NODE:
|
||||
$parent->appendChild( $parent->ownerDocument->importNode( $node ) );
|
||||
break;
|
||||
|
||||
/* These types of nodes are ignored */
|
||||
case XML_DOCUMENT_TYPE_NODE: // DOMDocumentType
|
||||
case XML_ATTRIBUTE_NODE: // DOMAttr
|
||||
case XML_ENTITY_REF_NODE: // DOMEntityReference
|
||||
case XML_ENTITY_NODE: // DOMEntity
|
||||
case XML_PI_NODE: // DOMProcessingInstruction
|
||||
case XML_COMMENT_NODE: // DOMComment
|
||||
case XML_DOCUMENT_NODE: // DOMDocument but not a HTML document
|
||||
case XML_DOCUMENT_FRAG_NODE: // DOMDocumentFragment
|
||||
case XML_NOTATION_NODE: // DOMNotation
|
||||
case XML_DTD_NODE:
|
||||
case XML_ELEMENT_DECL_NODE:
|
||||
case XML_ATTRIBUTE_DECL_NODE:
|
||||
case XML_ENTITY_DECL_NODE:
|
||||
case XML_NAMESPACE_DECL_NODE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop child nodes of a node and parse them
|
||||
*
|
||||
* @param \DOMNodeList $children The child nodes from the source document
|
||||
* @param \DOMNode $parent The node from the new document which will be the parent of all these nodes
|
||||
* @return void
|
||||
*/
|
||||
public function _parseDomNodeList( \DOMNodeList $children, \DOMNode $parent )
|
||||
{
|
||||
foreach ( $children as $child )
|
||||
{
|
||||
$this->_parseDomNode( $child, $parent );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get body contents from document
|
||||
*
|
||||
* @param \DOMDocument $document The document
|
||||
* @return string
|
||||
*/
|
||||
public static function getDocumentBody( \DOMDocument $document )
|
||||
{
|
||||
return $document->getElementsByTagName('body')->item(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get body contents from document
|
||||
*
|
||||
* @param \DOMDocument $document The document
|
||||
* @return string
|
||||
*/
|
||||
public static function getDocumentBodyContents( \DOMDocument $document )
|
||||
{
|
||||
if ( $body = static::getDocumentBody( $document ) )
|
||||
{
|
||||
return \substr( $document->saveHTML( $body ), 6, -7 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Encrypt Text
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 17 Apr 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -30,7 +30,7 @@ class _Encrypt
|
||||
*/
|
||||
public static function key()
|
||||
{
|
||||
return md5( \IPS\Settings::i()->sql_pass . \IPS\Settings::i()->sql_database );
|
||||
return \IPS\TEXT_ENCRYPTION_KEY ?: md5( \IPS\Settings::i()->sql_pass . \IPS\Settings::i()->sql_database );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Definition caching for HTML Purifier not on disk
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 25 Oct 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief A HTMLPurifier Attribute Definition used for attributes which must be internal URLs
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 21 Mar 2016
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* A HTMLPurifier Attribute Definition used for attributes which must be internal URLs
|
||||
*/
|
||||
class _HtmlPurifierInternalLinkDef extends \HTMLPurifier_AttrDef_URI
|
||||
{
|
||||
/**
|
||||
* @brief Allowed bases
|
||||
*/
|
||||
protected $allowedBases = NULL;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param bool $embeds_resource Does the URI here result in an extra HTTP request?
|
||||
* @param array|null $allowedBases If an array is provided, only URLs with the query strings set allowed - for example array( array( 'app' => 'core', 'module' => 'members', 'controller' => 'profile' ) ) will only allow URLs to profiles. If NULL, any internal URL beside the open proxy and attachment downloads is allowed
|
||||
*/
|
||||
public function __construct( $embeds_resource = false, $allowedBases = NULL )
|
||||
{
|
||||
$this->allowedBases = $allowedBases;
|
||||
return parent::__construct( $embeds_resource );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @param string $uri
|
||||
* @param \HTMLPurifier_Config $config
|
||||
* @param \HTMLPurifier_Context $context
|
||||
* @return bool|string
|
||||
*/
|
||||
public function validate($uri, $config, $context)
|
||||
{
|
||||
/* Create the URL */
|
||||
$url = \IPS\Http\Url::createFromString( str_replace( '%7B___base_url___%7D/', \IPS\Settings::i()->base_url, $uri ) );
|
||||
|
||||
/* If it's not internal, we can stop now */
|
||||
if ( !( $url instanceof \IPS\Http\Url\Internal ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* If we have allowed templates, check those... */
|
||||
if ( $this->allowedBases )
|
||||
{
|
||||
$isOkay = FALSE;
|
||||
$queryString = $url->isFriendly ? ( $url->hiddenQueryString + $url->queryString ) : $url->queryString;
|
||||
|
||||
foreach ( $this->allowedBases as $requiredQueryString )
|
||||
{
|
||||
$queryStringDiff = array_diff( $requiredQueryString, $queryString );
|
||||
if ( empty( $queryStringDiff ) )
|
||||
{
|
||||
$isOkay = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$isOkay )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise exclude the open proxy */
|
||||
if ( ( isset( $url->queryString['section'] ) and $url->queryString['section'] == 'redirect' ) or ( isset( $url->queryString['controller'] ) and $url->queryString['controller'] == 'redirect' ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* And exclude the attachments downloading gateway */
|
||||
if( mb_strpos( preg_replace( '#/{1,}#', '/', $url->data['path'] ), 'applications/core/interface/file/' ) !== FALSE )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* And exclude uploaded files (we do this by checking the beginning of the (protocol-relative) URL against all upload configurations) */
|
||||
$url = '//' . mb_substr( (string) $url, mb_strpos( (string) $url, '://' ) + 3 );
|
||||
|
||||
foreach( static::getUploadUrls() as $uploadBaseUrl )
|
||||
{
|
||||
if( mb_strpos( $url, '//' . mb_substr( $uploadBaseUrl, mb_strpos( $uploadBaseUrl, '://' ) + 3 ) ) === 0 )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? We can pass up */
|
||||
return parent::validate( $uri, $config, $context );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uploaded file base URLs (i.e. URL to uploads directory)
|
||||
*/
|
||||
protected static $uploadUrls = NULL;
|
||||
|
||||
/**
|
||||
* Fetch and cache upload URLs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function getUploadUrls()
|
||||
{
|
||||
if( static::$uploadUrls !== NULL )
|
||||
{
|
||||
return static::$uploadUrls;
|
||||
}
|
||||
|
||||
static::$uploadUrls = array();
|
||||
|
||||
foreach( \IPS\File::getConfigurations() as $configuration )
|
||||
{
|
||||
$class = \IPS\File::getClass( $configuration['id'] );
|
||||
|
||||
if( $class->baseUrl() !== NULL )
|
||||
{
|
||||
$url = new \IPS\Http\Url( $class->baseUrl() );
|
||||
|
||||
if( $url->isInternal )
|
||||
{
|
||||
static::$uploadUrls[ $class->baseUrl() ] = $class->baseUrl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static::$uploadUrls;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief A HTMLPurifier Attribute Definition for srcset attributes
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 4 May 2016
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* A HTMLPurifier Attribute Definition used for attributes which must be internal URLs
|
||||
*/
|
||||
class _HtmlPurifierSrcsetDef extends \HTMLPurifier_AttrDef_URI
|
||||
{
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @param string $srcset
|
||||
* @param \HTMLPurifier_Config $config
|
||||
* @param \HTMLPurifier_Context $context
|
||||
* @return bool|string
|
||||
*/
|
||||
public function validate($srcset, $config, $context)
|
||||
{
|
||||
$return = array();
|
||||
|
||||
foreach ( explode( ',', $srcset ) as $src )
|
||||
{
|
||||
$exploded = explode( ' ', trim( $src ) );
|
||||
$uri = array_shift( $exploded );
|
||||
$descriptor = implode( ' ', $exploded );
|
||||
|
||||
$validated = parent::validate( $uri, $config, $context );
|
||||
if ( $validated !== FALSE )
|
||||
{
|
||||
$return[] = $validated . ( $descriptor ? ( ' ' . $descriptor ) : '' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $return ) )
|
||||
{
|
||||
return implode( ', ', $return );
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief A HTMLPurifier Attribute Definition which imitates HTMLPurifier_AttrDef_Switch but allows checking if certain attributes exist
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 4 May 2016
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* A HTMLPurifier Attribute Definition which imitates HTMLPurifier_AttrDef_Switch but allows checking if certain attributes exist
|
||||
*/
|
||||
class _HtmlPurifierSwitchAttrDef extends \HTMLPurifier_AttrDef_Switch
|
||||
{
|
||||
public $required = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Attributes
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $tag The tag name to check
|
||||
* @param array $attributes The attributes to check on
|
||||
* @param \HTMLPurifier_AttrDef $with_tag If $tag matches and all $attributes are present, this definition will be used
|
||||
* @param \HTMLPurifier_AttrDef $without_tag Otherwise, this definition will be used
|
||||
*/
|
||||
public function __construct( $tag, $attributes, $with_tag, $without_tag )
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
parent::__construct( $tag, $with_tag, $without_tag );
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*
|
||||
* @param string $string
|
||||
* @param \HTMLPurifier_Config $config
|
||||
* @param \HTMLPurifier_Context $context
|
||||
* @return bool|string
|
||||
*/
|
||||
public function validate( $string, $config, $context )
|
||||
{
|
||||
$token = $context->get('CurrentToken', true);
|
||||
|
||||
if ( count( array_diff( $this->attributes, array_keys( $token->attr ) ) ) )
|
||||
{
|
||||
return $this->withoutTag->validate( $string, $config, $context );
|
||||
}
|
||||
|
||||
return parent::validate( $string, $config, $context );
|
||||
}
|
||||
}
|
||||
+165
-42
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Legacy Text Parser
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 12 Jun 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -182,7 +182,8 @@ class _LegacyParser
|
||||
}
|
||||
|
||||
/* Grab a new parser object */
|
||||
$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, 1 );
|
||||
\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 ) );
|
||||
|
||||
@@ -221,6 +222,7 @@ class _LegacyParser
|
||||
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 />' ) ) )
|
||||
@@ -229,14 +231,32 @@ class _LegacyParser
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \stristr( $value, '<br>' ) || \stristr( $value, '<br />' ) )
|
||||
if ( \stristr( $value, '<br>' ) || \stristr( $value, '<br />' ) || \stristr( $value, '<p>' ) )
|
||||
{
|
||||
/* Protect code tags first... */
|
||||
$value = preg_replace_callback( "/\[code.*?\](.+?)\[\/code\]/ims", function( $match ) use ( &$codeBlocks ) {
|
||||
$hash = md5( uniqid() );
|
||||
$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( uniqid() );
|
||||
$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 );
|
||||
}
|
||||
}
|
||||
|
||||
$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 );
|
||||
@@ -288,8 +308,13 @@ class _LegacyParser
|
||||
/* Old char conversions */
|
||||
$value = str_replace( " ", " ", $value );
|
||||
$value = str_replace( "'", "'", $value );
|
||||
$value = str_replace( "&;", "&", $value );
|
||||
$value = str_replace( "<#EMO_DIR>", "<#EMO_DIR#>", $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 );
|
||||
@@ -299,6 +324,13 @@ class _LegacyParser
|
||||
$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 );
|
||||
@@ -312,10 +344,26 @@ class _LegacyParser
|
||||
|
||||
$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 );
|
||||
@@ -340,13 +388,6 @@ class _LegacyParser
|
||||
$value = preg_replace( "#<blockquote>(.+?)</blockquote>#is" , "[indent]\\1[/indent]", $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 );
|
||||
}
|
||||
|
||||
/* 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 );
|
||||
@@ -366,10 +407,17 @@ class _LegacyParser
|
||||
$value = str_ireplace( "?>" , "?>" , $value );
|
||||
|
||||
/* Fix bbcode attributes */
|
||||
$value = preg_replace_callback( "/\[([^\]]+?)=(\"|'|'|'|")([^\]]+?)(\"|'|'|'|")\]/", function( $matches ) {
|
||||
/* We strip the enclosing quotes and we strip any trailing ';' */
|
||||
return "[" . $matches[1] . "=" . rtrim( $matches[3], ';' ) . "]";
|
||||
}, $value );
|
||||
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 )
|
||||
@@ -382,7 +430,7 @@ class _LegacyParser
|
||||
|
||||
/* Build the regex */
|
||||
$regex = "/\[(?:{$code['bbcode_tag']}" . ( $code['bbcode_aliases'] ? '|' . str_replace( ',', '|', $code['bbcode_aliases'] ) : '' ) . ")" .
|
||||
( $code['bbcode_useoption'] ? "=(.+?)" : '' ) . "\]" .
|
||||
( $code['bbcode_useoption'] ? ( $code['bbcode_optional_option'] ? "(?:=(.+?))?" : "=(.+?)" ) : '' ) . "\]" .
|
||||
( $code['bbcode_single_tag'] ? '' : ( "(.+?)\[\/(?:{$code['bbcode_tag']}" . ( $code['bbcode_aliases'] ? '|' . str_replace( ',', '|', $code['bbcode_aliases'] ) : '' ) . ")\]" ) ) .
|
||||
"/ims";
|
||||
|
||||
@@ -460,6 +508,7 @@ class _LegacyParser
|
||||
|
||||
/* Now fix shared media */
|
||||
$value = preg_replace_callback( "/\[sharedmedia=(.+?):(.+?):(.+?)\]/ims", function( $matches ) {
|
||||
|
||||
switch( $matches[1] )
|
||||
{
|
||||
case 'calendar':
|
||||
@@ -474,15 +523,20 @@ class _LegacyParser
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'gallery':
|
||||
/* Make sure gallery is enabled first */
|
||||
if ( \IPS\Application::appIsEnabled( 'gallery' ) === FALSE )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if( $matches[2] == 'images' )
|
||||
{
|
||||
try
|
||||
{
|
||||
$image = \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_id=?', (int) $matches[3] ) )->first();
|
||||
$url = \IPS\Http\Url::internal( "app=gallery&module=gallery&controller=view&id={$image['image_id']}", 'front', 'gallery_image', $image['image_caption_seo'] );
|
||||
return "<p><a href='{$url}'>{$url}</a></p>";
|
||||
$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 )
|
||||
{
|
||||
@@ -493,9 +547,8 @@ class _LegacyParser
|
||||
{
|
||||
try
|
||||
{
|
||||
$album = \IPS\Db::i()->select( '*', 'gallery_albums', array( 'album_id=?', (int) $matches[3] ) )->first();
|
||||
$url = \IPS\Http\Url::internal( "app=gallery&module=gallery&controller=browse&album={$album['album_id']}", 'front', 'gallery_album', $album['album_name_seo'] );
|
||||
return "<p><a href='{$url}'>{$url}</a></p>";
|
||||
$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(), FALSE, TRUE ) );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
@@ -503,7 +556,7 @@ class _LegacyParser
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'downloads':
|
||||
try
|
||||
{
|
||||
@@ -516,12 +569,12 @@ class _LegacyParser
|
||||
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
|
||||
{
|
||||
@@ -548,6 +601,12 @@ class _LegacyParser
|
||||
$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();
|
||||
|
||||
@@ -597,8 +656,26 @@ class _LegacyParser
|
||||
/* 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(.+?)href=(?<![\'"])(.*?)(?!["\'])([ >])#is', function( $matches ){
|
||||
return "<a" . $matches[1] . " href='" . str_replace( array( "'", '"' ), '', $matches[2] ) . "'" . $matches[3];
|
||||
$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 );
|
||||
@@ -616,17 +693,21 @@ class _LegacyParser
|
||||
|
||||
$replacement = '<!--url{' . $c . '}-->';
|
||||
|
||||
/* 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' ) and ! mb_strpos( $matches[0][ $k ], 'nomediaparse' ) AND $response = \IPS\Text\Parser::embeddableMedia( $matches[1][ $k ], TRUE ) )
|
||||
try
|
||||
{
|
||||
$urls[ $c ] = $response;
|
||||
/* 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 );
|
||||
preg_match_all( '#<img[^>]+?src=[\'"](.+?)["\'].*?>#is', $value, $matches );
|
||||
|
||||
foreach( $matches[0] as $m )
|
||||
{
|
||||
@@ -641,7 +722,7 @@ class _LegacyParser
|
||||
|
||||
$done = array();
|
||||
|
||||
foreach( $matches[0] as $m )
|
||||
foreach( $matches[1] as $m )
|
||||
{
|
||||
if( in_array( $m, $done ) )
|
||||
{
|
||||
@@ -670,7 +751,11 @@ class _LegacyParser
|
||||
$url = preg_replace( "/#entry(\d+)/", $sep . 'do=findComment' . $sep . "comment=$1", $m );
|
||||
}
|
||||
|
||||
$me = \IPS\Text\Parser::embeddableMedia( $url, TRUE );
|
||||
try
|
||||
{
|
||||
$me = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $url, FALSE, TRUE ) );
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
|
||||
if( $me )
|
||||
@@ -705,7 +790,7 @@ class _LegacyParser
|
||||
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']}'></a>";
|
||||
$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
|
||||
{
|
||||
@@ -721,6 +806,9 @@ class _LegacyParser
|
||||
$value = '<p>' . $value . '</p>';
|
||||
$value = str_replace( array( '<br>', '<br />' ), '</p><p>', $value );
|
||||
}
|
||||
|
||||
/* https://community.invisionpower.com/4bugtrack/active-reports/4081-editing-posts-ads-extra-spacing-r6742/ */
|
||||
$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 );
|
||||
@@ -766,7 +854,7 @@ class _LegacyParser
|
||||
$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 )
|
||||
$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 );
|
||||
@@ -866,7 +954,7 @@ class _LegacyParser
|
||||
|
||||
if ( $match[3] )
|
||||
{
|
||||
$return[] = " name='{$match[3]}'";
|
||||
$return[] = " name='" . static::_cleanQuoteName( $match[3] ) . "'";
|
||||
}
|
||||
|
||||
if ( $match[1] )
|
||||
@@ -895,7 +983,7 @@ class _LegacyParser
|
||||
|
||||
if( count( $matches[1] ) )
|
||||
{
|
||||
preg_match( "/data-author=['\"](.+?)[\"']/i", $matches[1], $author );
|
||||
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 );
|
||||
|
||||
@@ -907,7 +995,8 @@ class _LegacyParser
|
||||
{
|
||||
$pieces = explode( '_', $this->attachClass );
|
||||
|
||||
$parameters['data-ipsquote-contenttype'] = $pieces[0];
|
||||
$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;
|
||||
}
|
||||
@@ -968,6 +1057,24 @@ class _LegacyParser
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
@@ -1001,7 +1108,7 @@ class _LegacyParser
|
||||
{
|
||||
return '[code]' . rtrim( str_replace( "</span>", '', preg_replace( "#<span style='.+?'>#is", "", stripslashes( $matches[2] ) ) ) ) . '[/code]';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert download manager screenshot URLs
|
||||
*
|
||||
@@ -1028,4 +1135,20 @@ class _LegacyParser
|
||||
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;
|
||||
}
|
||||
}
|
||||
+3094
-2166
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user