1
0
mirror of https://github.com/e107inc/e107.git synced 2025-01-16 20:28:28 +01:00
php-e107/e107_handlers/e_parse_class.php
2025-01-16 11:23:25 -08:00

5922 lines
151 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/*
* e107 website system
*
* Copyright (C) 2008-2016 e107 Inc (e107.org)
* Released under the terms and conditions of the
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
*
* Text processing and parsing functions
*
*/
if (!defined('e107_INIT'))
{
exit();
}
define('E_NL', chr(2));
/**
*
*/
class e_parse
{
/**
* Determine how to handle utf-8.
* 0 = 'do nothing'
* 1 = 'use mb_string'
* 2 = emulation
*
* @var integer
*/
private $multibyte = false; // previously $utfAction
private $pref; // core prefs used in toHTML.
// 'Hooked' parsers (array)
private $e_hook = array();
// Used in search_class.php (move??)
public $search = array('&amp;#039;', '&#039;', '&#39;', '&quot;', 'onerror', '&gt;', '&amp;quot;', ' & ');
public $replace = array("'", "'", "'", '"', 'one<i></i>rror', '>', '"', ' &amp; ');
// Set to TRUE or FALSE once it has been calculated
protected $e_highlighting;
// Highlight query
protected $e_query;
private $thumbWidth = 100;
private $thumbHeight = 0;
private $thumbCrop = 0;
private $thumbEncode = 0;
private $staticCount = 0;
protected $staticUrl;
protected $staticUrlMap = [];
/** @var array Stored relative paths - used by replaceConstants() */
private $relativePaths = [];
// BBcode that contain preformatted code.
private $preformatted = array('html', 'markdown');
// Set up the defaults
private $e_optDefault = array(
// default context: reflects legacy settings (many items enabled)
'context' => 'OLDDEFAULT',
//
'fromadmin' => false,
// Enable emote display
'emotes' => true,
// Convert defines(constants) within text.
'defs' => false,
// replace all {e_XXX} constants with their e107 value - 'rel' or 'abs'
'constants' => false,
// Enable hooked parsers
'hook' => true,
// Allow scripts through (new for 0.8)
'scripts' => true,
// Make links clickable
'link_click' => true,
// Substitute on clickable links (only if link_click == TRUE)
'link_replace' => true,
// Parse shortcodes - TRUE enables parsing
'parse_sc' => false,
// remove HTML tags.
'no_tags' => false,
// Restore entity form of quotes and such to single characters - TRUE disables
'value' => false,
// Line break compression - TRUE removes newline characters
'nobreak' => false,
// Retain newlines - wraps to \n instead of <br /> if TRUE (for non-HTML email text etc)
'retain_nl' => false
);
// Super modifiers override default option values
private $e_SuperMods = array(
//text is part of a title (e.g. news title)
'TITLE' =>
array(
'nobreak' => true, 'retain_nl' => true, 'link_click' => false, 'emotes' => false, 'defs' => true, 'parse_sc' => true
),
'TITLE_PLAIN' =>
array(
'nobreak' => true, 'retain_nl' => true, 'link_click' => false, 'emotes' => false, 'defs' => true, 'parse_sc' => true, 'no_tags' => true
),
//text is user-entered (i.e. untrusted) and part of a title (e.g. forum title)
'USER_TITLE' =>
array(
'nobreak' => true, 'retain_nl' => true, 'link_click' => false, 'scripts' => false, 'emotes' => false, 'hook' => false
),
// text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability
'E_TITLE' =>
array(
'nobreak' => true, 'retain_nl' => true, 'defs' => true, 'parse_sc' => true, 'emotes' => false, 'scripts' => false, 'link_click' => false
),
// text is part of the summary of a longer item (e.g. content summary)
'SUMMARY' =>
array(
'defs' => true, 'constants' => 'full', 'parse_sc' => true
),
// text is the description of an item (e.g. download, link)
'DESCRIPTION' =>
array(
'defs' => true, 'constants' => 'full', 'parse_sc' => true
),
// text is 'body' or 'bulk' text (e.g. custom page body, content body)
'BODY' =>
array(
'defs' => true, 'constants' => 'full', 'parse_sc' => true
),
// text is parsed by the Wysiwyg editor. eg. TinyMce
'WYSIWYG' =>
array(
'hook' => false, 'link_click' => false, 'link_replace' => false, 'retain_nl' => true
),
// text is user-entered (i.e. untrusted)'body' or 'bulk' text (e.g. custom page body, content body)
'USER_BODY' =>
array(
'constants' => 'full', 'scripts' => false, 'nostrip' => false
),
// text is 'body' of email or similar - being sent 'off-site' so don't rely on server availability
'E_BODY' =>
array(
'defs' => true, 'constants' => 'full', 'parse_sc' => true, 'emotes' => false, 'scripts' => false, 'link_click' => false
),
// text is text-only 'body' of email or similar - being sent 'off-site' so don't rely on server availability
'E_BODY_PLAIN' =>
array(
'defs' => true, 'constants' => 'full', 'parse_sc' => true, 'emotes' => false, 'scripts' => false, 'link_click' => false, 'retain_nl' => true, 'no_tags' => true
),
// text is the 'content' of a link (A tag, etc)
'LINKTEXT' =>
array(
'nobreak' => true, 'retain_nl' => true, 'link_click' => false, 'emotes' => false, 'hook' => false, 'defs' => true, 'parse_sc' => true
),
// text is used (for admin edit) without fancy conversions or html.
'RAWTEXT' =>
array(
'nobreak' => true, 'retain_nl' => true, 'link_click' => false, 'emotes' => false, 'hook' => false, 'no_tags' => true
),
'NODEFAULT' =>
array('context' => false, 'fromadmin' => false, 'emotes' => false, 'defs' => false, 'constants' => false, 'hook' => false,
'scripts' => false, 'link_click' => false, 'link_replace' => false, 'parse_sc' => false, 'no_tags' => false, 'value' => false,
'nobreak' => false, 'retain_nl' => false
)
);
// Individual modifiers change the current context
private $e_Modifiers = array(
'emotes_off' => array('emotes' => false),
'emotes_on' => array('emotes' => true),
'no_hook' => array('hook' => false),
'do_hook' => array('hook' => true),
// New for 0.8
'scripts_off' => array('scripts' => false),
// New for 0.8
'scripts_on' => array('scripts' => true),
'no_make_clickable' => array('link_click' => false),
'make_clickable' => array('link_click' => true),
'no_replace' => array('link_replace' => false),
// Replace text of clickable links (only if make_clickable option set)
'replace' => array('link_replace' => true),
// No path replacement
'consts_off' => array('constants' => false),
// Relative path replacement
'consts_rel' => array('constants' => 'rel'),
// Absolute path replacement
'consts_abs' => array('constants' => 'abs'),
// Full path replacement
'consts_full' => array('constants' => 'full'),
// No shortcode parsing
'scparse_off' => array('parse_sc' => false),
'scparse_on' => array('parse_sc' => true),
// Strip tags
'no_tags' => array('no_tags' => true),
// Leave tags
'do_tags' => array('no_tags' => false),
'fromadmin' => array('fromadmin' => true),
'notadmin' => array('fromadmin' => false),
// entity replacement
'er_off' => array('value' => false),
'er_on' => array('value' => true),
// Decode constant if exists
'defs_off' => array('defs' => false),
'defs_on' => array('defs' => true),
'dobreak' => array('nobreak' => false),
'nobreak' => array('nobreak' => true),
// Line break using \n
'lb_nl' => array('retain_nl' => true),
// Line break using <br />
'lb_br' => array('retain_nl' => false),
// Legacy option names below here - discontinue later
'retain_nl' => array('retain_nl' => true),
'defs' => array('defs' => true),
'parse_sc' => array('parse_sc' => true),
'constants' => array('constants' => 'rel'),
'value' => array('value' => true),
'wysiwyg' => array('wysiwyg' => true)
);
/**
* @var DOMDocument
*/
private $domObj;
private $isHtml = false;
private $bootstrap;
private $fontawesome;
private $modRewriteMedia;
private $removedList = array();
private $nodesToDelete = array();
private $nodesToConvert = array();
private $nodesToDisableSC = array();
private $pathList = array();
private $allowedAttributes = array();
private $badAttrValues = array();
private $replaceAttrValues = array();
private $allowedTags = array();
private $scriptTags = array();
private $scriptAttributes = array();
private $blockTags = array();
private $scriptAccess = false; // nobody.
private $replaceVars;
private $replaceUnset;
/**
* Constructor - keep it public for backward compatibility
* still some new e_parse() in the core
*
*/
public function __construct()
{
// initialise the type of UTF-8 processing methods depending on PHP version and mb string extension
$this->domObj = new DOMDocument('1.0', 'utf-8');
$this->init();
$this->compileAttributeDefaults();
}
/**
* @param string $type
* @return array
*/
public function getModifierList($type = '')
{
if ($type === 'super')
{
return $this->e_SuperMods;
}
return $this->e_Modifiers;
}
/**
* Initialise the type of UTF-8 processing methods depending on PHP version and mb string extension.
* Note: mb string is required during installation of e107.
* NOTE: can't be called until CHARSET is known
* but we all know that it is UTF-8 now
*
* @return void
*/
public function setMultibyte($bool)
{
if ($bool === false)
{
$this->multibyte = false;
return null;
}
if (extension_loaded('mbstring'))
{
$this->multibyte = true;
mb_internal_encoding('UTF-8');
}
}
/**
* Returns the length of the given string.
* Unicode (UTF-8) analogue of standard @link http://php.net/strlen strlen PHP function.
*
* @param string $str The UTF-8 encoded string being measured for length.
* @return integer The length (amount of UTF-8 characters) of the string on success, and 0 if the string is empty.
*/
public function ustrlen($str)
{
if ($this->multibyte)
{
return mb_strlen($str);
}
return strlen($str);
// return strlen(utf8_decode($str));
}
/**
* Make a string lowercase.
* Unicode (UTF-8) analogue of standard @link http://php.net/strtolower strtolower PHP function.
*
* @param string $str The UTF-8 encoded string to be lowercased.
* @return string Specified string with all alphabetic characters converted to lowercase.
*/
public function ustrtolower($str)
{
if ($this->multibyte)
{
return mb_strtolower($str);
}
return strtolower($str);
}
/**
* Make a string uppercase.
* Unicode (UTF-8) analogue of standard @link http://php.net/strtoupper strtoupper PHP function.
*
* @param string $str The UTF-8 encoded string to be uppercased.
* @return string Specified string with all alphabetic characters converted to uppercase.
*/
public function ustrtoupper($str)
{
if ($this->multibyte)
{
return mb_strtoupper($str);
}
return strtoupper($str);
}
/**
* Find the position of the first occurrence of a case-sensitive UTF-8 encoded string.
* Unicode (UTF-8) analogue of standard @link http://php.net/strpos strpos PHP function.
*
* Returns the numeric position (offset in amount of UTF-8 characters)
* of the first occurrence of needle in the haystack string.
*
* @param string $haystack The UTF-8 encoded string being searched in.
* @param integer $needle The UTF-8 encoded string being searched for.
* @param integer $offset [optional] The optional offset parameter allows you to specify which character in haystack to start searching.
* The position returned is still relative to the beginning of haystack.
* @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE.
*/
public function ustrpos($haystack, $needle, $offset = 0)
{
if ($this->multibyte)
{
return mb_strpos($haystack, $needle, $offset);
}
return strpos($haystack, $needle, $offset);
}
/**
* Find the position of the last occurrence of a case-sensitive UTF-8 encoded string.
* Unicode (UTF-8) analogue of standard @link http://php.net/strrpos strrpos PHP function.
* Returns the numeric position (offset in amount of UTF-8 characters)
* of the last occurrence of needle in the haystack string.
*
* @param string $haystack The UTF-8 encoded string being searched in.
* @param integer $needle The UTF-8 encoded string being searched for.
* @param integer $offset [optional] - The optional offset parameter allows you to specify which character in haystack to start searching.
* The position returned is still relative to the beginning of haystack.
* @return integer|boolean Returns the position as an integer. If needle is not found, the function will return boolean FALSE.
*/
public function ustrrpos($haystack, $needle, $offset = 0)
{
if ($this->multibyte)
{
return mb_strrpos($haystack, $needle, $offset);
}
return strrpos($haystack, $needle, $offset);
}
/**
* Returns all of haystack starting from and including the first occurrence of needle to the end.
* Unicode (UTF-8) analogue of standard @link http://php.net/stristr stristr PHP function.
*
* @param string $haystack The UTF-8 encoded string to search in.
* @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
* @param bool $before_needle [optional] (PHP 5.3+) If TRUE, returns the part of the haystack before the first occurrence of the needle (excluding needle).
* @return string Returns the matched substring. If needle is not found, returns FALSE.
*/
public function ustristr($haystack, $needle, $before_needle = false)
{
if ($this->multibyte)
{
return mb_stristr($haystack, $needle, $before_needle);
}
return stristr($haystack, $needle, $before_needle);
}
/**
* Returns the portion of string specified by the start and length parameters.
* Unicode (UTF-8) analogue of standard @link http://php.net/substr substr PHP function.
*
* NOTE: May be subtle differences in return values dependent on which routine is used.
* Native substr() routine can return FALSE. mb_substr() and utf8_substr() just return an empty string.
*
* @param string $str The UTF-8 encoded string.
* @param integer $start Start of portion to be returned. Position is counted in amount of UTF-8 characters from the beginning of str.
* First character's position is 0. Second character position is 1, and so on.
* @param integer $length [optional] If length is given, the string returned will contain at most length characters beginning from start
* (depending on the length of string). If length is omitted, the rest of string from start will be returned.
* @return string The extracted UTF-8 encoded part of input string.
*/
public function usubstr($str, $start, $length = null)
{
if ($this->multibyte)
{
return ($length === null) ? mb_substr($str, $start) : mb_substr($str, $start, $length);
}
return substr($str, $start, $length);
}
/**
* Converts the supplied text (presumed to be from user input) to a format suitable for storing in a database table.
*
* @param mixed $data
* @param boolean $nostrip [optional] Assumes all data is GPC ($_GET, $_POST, $_COOKIE) unless indicate otherwise by setting this var to TRUE.
* If magic quotes is enabled on the server and you do not tell toDB() that the data is non GPC then slashes will be stripped when they should not be.
* @param boolean $no_encode [optional] This parameter should nearly always be FALSE. It is used by the save_prefs() function to preserve HTML content within prefs even when
* the save_prefs() function has been called by a non admin user / user without html posting permissions.
* @param boolean|string $mod [optional] model = admin-ui usage. The 'no_html' and 'no_php' modifiers blanket prevent HTML and PHP posting regardless of posting permissions. (used in logging)
* The 'pReFs' value is for internal use only, when saving prefs, to prevent sanitisation of HTML.
* @param mixed $parm [optional]
* @return mixed
* @todo complete the documentation of this essential method
*/
public function toDB($data = null, $nostrip = false, $no_encode = false, $mod = false, $parm = null)
{
$variableType = gettype($data);
if (($variableType !== 'string' && $variableType !== 'array') || $data === '0')
{
return $data;
}
if ($variableType === 'array')
{
$ret = array();
foreach ($data as $key => $var)
{
//Fix - sanitize keys as well
$key = str_replace(['"', "'"], ['&quot;', '&#039;'], $key);
$ret[$key] = $this->toDB($var, $nostrip, $no_encode, $mod, $parm);
}
return $ret;
}
if (MAGIC_QUOTES_GPC === true && $nostrip === false)
{
$data = stripslashes($data);
}
$core_pref = e107::getConfig();
if ($mod !== 'pReFs') //XXX We're not saving prefs.
{
$data = $this->preFilter($data); // used by bb_xxx.php toDB() functions. bb_code.php toDB() allows us to properly bypass HTML cleaning below.
$data = $this->cleanHtml($data); // clean it regardless of if it is text or html. (html could have missing closing tags)
if (($this->isHtml($data)) && strpos($mod, 'no_html') === false)
{
$this->isHtml = true;
// $data = $this->cleanHtml($data); // sanitize all html. (moved above to include everything)
$data = str_replace(array('%7B', '%7D'), array('{', '}'), $data); // fix for {e_XXX} paths.
}
// else // caused double-encoding of '&'
{
// $data = str_replace('&amp;','&',$data);
// $data = str_replace('<','&lt;',$data);
// $data = str_replace('>','&gt;',$data);
// $data = str_replace('&','&amp;',$data);
}
if (!check_class($core_pref->get('post_html', e_UC_MAINADMIN)))
{
$data = strip_tags($data); // remove tags from cleaned html.
$data = str_replace(array('[html]', '[/html]'), '', $data);
}
// $data = html_entity_decode($data, ENT_QUOTES, 'utf-8'); // Prevent double-entities. Fix for [code] - see bb_code.php toDB();
}
if (check_class($core_pref->get('post_html'))) /*$core_pref->is('post_html') && XXX preformecd by cleanHtml() */
{
$no_encode = true;
}
if ($parm !== null && is_numeric($parm) && !check_class($core_pref->get('post_html'), '', $parm))
{
$no_encode = false;
}
if ($no_encode === true && strpos($mod, 'no_html') === false)
{
$search = array('$', '"', "'", '\\', '<?');
$replace = array('&#036;', '&quot;', '&#039;', '&#092;', '&lt;?');
$ret = str_replace($search, $replace, $data);
}
else // add entities for everything. we want to save the code.
{
$search = array('&gt;', '&lt;');
$replace = array('>', '<');
$data = str_replace($search, $replace, $data); // prevent &amp;gt; etc.
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
$data = str_replace('\\', '&#092;', $data);
$ret = preg_replace("/&amp;#(\d*?);/", "&#\\1;", $data);
}
// XXX - php_bbcode has been deprecated.
if ((strpos($mod, 'no_php') !== false) || !check_class($core_pref->get('php_bbcode')))
{
$ret = preg_replace("#\[(php)#i", "&#91;\\1", $ret);
}
// Don't allow hooks to mess with prefs.
if ($mod !== 'model')
{
return $ret;
}
/**
* e_parse hook
*/
$eParseList = $core_pref->get('e_parse_list');
if (!empty($eParseList))
{
$opts = array(
'nostrip' => $nostrip,
'noencode' => $no_encode,
'type' => $parm['type'],
'field' => $parm['field']
);
foreach ($eParseList as $plugin)
{
$hookObj = e107::getAddon($plugin, 'e_parse');
if ($tmp = e107::callMethod($hookObj, 'toDB', $ret, $opts))
{
$ret = $tmp;
}
}
}
return $ret;
}
/**
* Check for umatched 'dangerous' HTML tags
* (these can destroy page layout where users are able to post HTML)
*
* @param string $data
* @param string $tagList - if empty, uses default list of input tags. Otherwise a CSV list of tags to check (any type)
*
* @return string TRUE if an unopened closing tag found
* FALSE if nothing found
* @deprecated
*/
public function htmlAbuseFilter($data, $tagList = '')
{
trigger_error('<b>' . __METHOD__ . ' is deprecated. Use $tp->cleanHtml() instead.</b>', E_USER_WARNING); // NO LAN
return $data;
}
/**
* @deprecated
* Checks a string for potentially dangerous HTML tags, including malformed tags
*
*/
public function dataFilter($data, $mode = 'bbcode')
{
trigger_error('$tp->dateFilter() is deprecated. Use $tp->filter() instead.', E_USER_WARNING);
return $data;
}
/**
* Processes data as needed before its written to the DB.
* Currently gives bbcodes the opportunity to do something
*
* @param $data string - data about to be written to DB
* @return string - modified data
*/
public function preFilter($data)
{
if (!$this->isBBcode($data))
{
return $data;
}
return e107::getBB()->parseBBCodes($data, defset('USERID'), 'default', 'PRE'); // $postID = logged in user here
}
/**
* Takes a multi-dimensional array and converts the keys to a list of routing paths.
* paths are the key and value are the top most key.
*
* @param array $array
* @return array
*/
public function toRoute($array)
{
$res = $this->_processRoute($array);
$tmp = explode("_#_", $res);
$ret = [];
foreach ($tmp as $v)
{
list($k) = explode('/', $v);
$ret[$v] = $k;
}
return $ret;
}
/**
* @param array $array
* @param string $prefix
* @return string
*/
private function _processRoute($array, $prefix = '')
{
$text = [];
if (is_array($array))
{
foreach ($array as $key => $val)
{
if ($tag = $this->_processRoute($val, $key . '/'))
{
$add = $tag;
}
else
{
$add = $key;
}
$text[] = $prefix . $add;
}
}
return implode('_#_', $text);
}
/**
* @param string $text
* @return array|string|string[]
*/
public function toForm($text)
{
if (empty($text)) // fix - handle proper 0, Space etc values.
{
return $text;
}
if (is_string($text) && strpos($text, '[html]') === 0)
{
// $text = $this->toHTML($text,true);
$search = array('&quot;', '&#039;', '&#092;', '&',); // '&' must be last.
$replace = array('"', "'", "\\", '&amp;');
// return htmlspecialchars_decode($text);
$text = str_replace($search, $replace, $text);
// return $text;
//$text = htmlentities($text,ENT_NOQUOTES, "UTF-8");
// return $text;
}
// return htmlentities($text);
$search = array('&#036;', '&quot;', '<', '>', '+');
$replace = array('$', '"', '&lt;', '&gt;', '%2B');
$text = str_replace($search, $replace, $text);
if (is_string($text) && e107::wysiwyg() !== true)
{
// fix for utf-8 issue with html_entity_decode(); ???
$text = urldecode($text);
// $text = str_replace("&nbsp;", " ", $text);
}
return $text;
}
/**
* @param $text
* @return array|string
*/
public function post_toForm($text)
{
if (is_array($text))
{
$arr = array();
foreach ($text as $key => $value)
{
$key = $this->post_toForm($key);
$arr[$key] = $this->post_toForm($value);
}
return $arr;
}
$text = (string) $text;
if (MAGIC_QUOTES_GPC == true)
{
$text = stripslashes($text);
}
return str_replace(array("'", '"', '<', '>'), array('&#039;', '&quot;', '&lt;', '&gt;'), $text);
}
/**
* @param string $text
* @param $original_author
* @param string $extra
* @param bool $mod
* @return string
*/
public function post_toHTML($text, $original_author = false, $extra = '', $mod = false)
{
$text = $this->toDB($text, false, false, $mod, $original_author);
return $this->toHTML($text, true, $extra);
}
/**
* @param $text - template to parse.
* @param boolean $parseSCFiles - parse core 'single' shortcodes
* @param object|array $extraCodes - shortcode class containing sc_xxxxx methods or an array of key/value pairs or legacy shortcode content (eg. content within .sc)
* @param object $eVars - XXX more info needed.
* @return string
*/
public function parseTemplate($text, $parseSCFiles = true, $extraCodes = null, $eVars = null)
{
if (!is_bool($parseSCFiles))
{
trigger_error('$parseSCFiles in parseTemplate() was given incorrect data');
}
return e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
}
/**
* @experimental
* @param string $text
* @param bool $parseSCFiles
* @param object|array $extraCodes
* @param object $eVars
* @return string
*/
public function parseSchemaTemplate($text, $parseSCFiles = true, $extraCodes = null, $eVars = null)
{
$parse = e107::getScParser();
$parse->setMode('schema');
$text = e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
$text = str_replace('<!-- >', '', $text); // cleanup
$parse->setMode('default');
return $text;
}
/**
* Simple parser
*
* @param string $template
* @param e_vars|array $vars
* @param string $replaceUnset string to be used if replace variable is not set, false - don't replace
* @return string parsed content
*/
public function simpleParse($template, $vars, $replaceUnset = '')
{
$this->replaceVars = $vars;
$this->replaceUnset = $replaceUnset;
return preg_replace_callback("#\{([\w]+)\}#", array($this, 'simpleReplace'), $template);
}
/**
* @param $tmp
* @return mixed|string|null
*/
protected function simpleReplace($tmp)
{
$unset = ($this->replaceUnset !== false ? $this->replaceUnset : $tmp[0]);
if (is_array($this->replaceVars))
{
$this->replaceVars = new e_vars($this->replaceVars);
//return ($this->replaceVars[$key] !== null ? $this->replaceVars[$key]: $unset);
}
$key = $tmp[1]; // PHP7 fix.
return (!empty($this->replaceVars) && ($this->replaceVars->$key !== null)) ? $this->replaceVars->$key : $unset; // Doesn't work.
}
/**
* @param $str
* @param $width
* @param string $break
* @param string $nobreak
* @param string $nobr
* @param false $utf
* @return string
* @todo find a modern replacement
*/
public function htmlwrap($str, $width, $break = "\n", $nobreak = 'a', $nobr = 'pre', $utf = false)
{
/*
Pretty well complete rewrite to try and handle utf-8 properly.
Breaks each utf-8 'word' every $width characters max. If possible, breaks after 'safe' characters.
$break is the character inserted to flag the break.
$nobreak is a list of tags within which word wrap is to be inactive
*/
//TODO handle htmlwrap somehow
//return $str;
// Don't wrap if non-numeric width
$width = (int) $width;
// And trap stupid wrap counts
if ($width < 6)
{
return $str;
}
// Transform protected element lists into arrays
$nobreak = explode(' ', strtolower($nobreak));
// Variable setup
$innbk = array();
$drain = '';
// List of characters it is "safe" to insert line-breaks at
// It is not necessary to add < and > as they are automatically implied
$lbrks = "/?!%)-}]\\\"':;&";
// Is $str a UTF8 string?
if ($utf || strtolower(CHARSET) === 'utf-8')
{
// 0x1680, 0x180e, 0x2000-0x200a, 0x2028, 0x205f, 0x3000 are 'non-ASCII' Unicode UCS-4 codepoints - see http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
// All convert to 3-byte utf-8 sequences:
// 0x1680 0xe1 0x9a 0x80
// 0x180e 0xe1 0xa0 0x8e
// 0x2000 0xe2 0x80 0x80
// -
// 0x200a 0xe2 0x80 0x8a
// 0x2028 0xe2 0x80 0xa8
// 0x205f 0xe2 0x81 0x9f
// 0x3000 0xe3 0x80 0x80
$utf8 = 'u';
$whiteSpace = '#([\x20|\x0c]|[\xe1][\x9a][\x80]|[\xe1][\xa0][\x8e]|[\xe2][\x80][\x80-\x8a,\xa8]|[\xe2][\x81][\x9f]|[\xe3][\x80][\x80]+)#';
// Have to explicitly enumerate the whitespace chars, and use non-utf-8 mode, otherwise regex fails on badly formed utf-8
}
else
{
$utf8 = '';
// For non-utf-8, can use a simple match string
$whiteSpace = '#(\s+)#';
}
// Start of the serious stuff - split into HTML tags and text between
$content = preg_split('#(<.*?' . '>)#mis', $str, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($content as $value)
{
if ($value[0] === '<')
{
// We are within an HTML tag
// Create a lowercase copy of this tag's contents
$lvalue = strtolower(substr($value, 1, -1));
if ($lvalue)
{
// Tag of non-zero length
// If the first character is not a / then this is an opening tag
if ($lvalue[0] !== '/')
{
// Collect the tag name
preg_match("/^(\w*?)(\s|$)/", $lvalue, $t);
// If this is a protected element, activate the associated protection flag
if (in_array($t[1], $nobreak))
{
array_unshift($innbk, $t[1]);
}
}
else
{
// Otherwise this is a closing tag
// If this is a closing tag for a protected element, unset the flag
if (in_array(substr($lvalue, 1), $nobreak))
{
reset($innbk);
foreach ($innbk as $key => $tag)
{
if (substr($lvalue, 1) == $tag)
{
unset($innbk[$key]);
break;
}
}
$innbk = array_values($innbk);
}
}
}
else
{
// Eliminate any empty tags altogether
$value = '';
}
// Else if we're outside any tags, and with non-zero length string...
}
elseif ($value)
{
// If unprotected...
if (!count($innbk))
{
// Use the ACK (006) ASCII symbol to replace all HTML entities temporarily
$value = str_replace("\x06", '', $value);
preg_match_all("/&([a-z\d]{2,7}|#\d{2,5});/i", $value, $ents);
$value = preg_replace("/&([a-z\d]{2,7}|#\d{2,5});/i", "\x06", $value);
// echo "Found block length ".strlen($value).': '.substr($value,20).'<br />';
// Split at spaces - note that this will fail if presented with invalid utf-8 when doing the regex whitespace search
// $split = preg_split('#(\s)#'.$utf8, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
$split = preg_split($whiteSpace, $value, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$value = '';
foreach ($split as $sp)
{
// echo "Split length ".strlen($sp).': '.substr($sp,20).'<br />';
$loopCount = 0;
while (strlen($sp) > $width)
{
// Enough characters that we may need to do something.
$pulled = '';
if ($utf8)
{
// Pull out a piece of the maximum permissible length
if (preg_match('#^((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,' . $width . '})(.?).*#s', $sp, $matches) == 0)
{
// Make any problems obvious for now
$value .= '[!<b>invalid utf-8: ' . $sp . '<b>!]';
$sp = '';
}
elseif (empty($matches[2]))
{
// utf-8 length is less than specified - treat as a special case
$value .= $sp;
$sp = '';
}
else
{
// Need to find somewhere to break the string
for ($i = strlen($matches[1]) - 1; $i >= 0; $i--)
{
if (strpos($lbrks, $matches[1][$i]) !== false)
{
break;
}
}
if ($i < 0)
{
// No 'special' break character found - break at the word boundary
$pulled = $matches[1];
}
else
{
$pulled = substr($sp, 0, $i + 1);
}
}
$loopCount++;
if ($loopCount > 20)
{
// Make any problems obvious for now
$value .= '[!<b>loop count exceeded: ' . $sp . '</b>!]';
$sp = '';
}
}
else
{
for ($i = min($width, strlen($sp)); $i > 0; $i--)
{
// No speed advantage to defining match character
if (strpos($lbrks, $sp[$i - 1]) !== false)
{
break;
}
}
if ($i == 0)
{
// No 'special' break boundary character found - break at the word boundary
$pulled = substr($sp, 0, $width);
}
else
{
$pulled = substr($sp, 0, $i);
}
}
if ($pulled)
{
$value .= $pulled . $break;
// Shorten $sp by whatever we've processed (will work even for utf-8)
$sp = substr($sp, strlen($pulled));
}
}
// Add in any residue
$value .= $sp;
}
// Put captured HTML entities back into the string
foreach ($ents[0] as $ent)
{
$value = preg_replace("/\x06/", $ent, $value, 1);
}
}
}
// Send the modified segment down the drain
$drain .= $value;
}
// Return contents of the drain
return $drain;
}
/**
* Universal text/bbcode/html truncate method.
* new in v2.3.1
*
* @param $text
* @param int $length
* @param string $ending
* @return string
*/
public function truncate($text, $length = 100, $ending = '...')
{
if ($this->isHtml($text))
{
return $this->html_truncate($text, $length, $ending);
}
if ($this->isBBcode($text))
{
$text = $this->toText($text);
}
return $this->text_truncate($text, $length, $ending);
}
/**
* @param string $text String to truncate.
* @param integer $length Length of returned string, including ellipsis.
* @param string $ending It will be used as Ending and appended to the trimmed string.
* @param boolean $exact If false, $text will not be cut mid-word
* @return string Trimmed string.
* @deprecated Soon to be made private. Use $tp->truncate() instead.
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
*
* Truncate a HTML string
*
* Cuts a string to the length of $length and adds the value of $ending if the text is longer than length.
*/
public function html_truncate($text, $length = 100, $ending = '...', $exact = true)
{
if ($this->ustrlen(preg_replace('/<.*?>/', '', $text)) <= $length)
{
return $text;
}
$totalLength = 0;
$openTags = array();
$truncate = '';
preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
foreach ($tags as $tag)
{
if (!$tag[2] || !preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/i', $tag[2]))
{
if (preg_match('/<[\w]+[^>]*>/', $tag[0]))
{
array_unshift($openTags, $tag[2]);
}
elseif (preg_match('/<\/([\w]+)[^>]*>/', $tag[0], $closeTag))
{
$pos = array_search($closeTag[1], $openTags);
if ($pos !== false)
{
array_splice($openTags, $pos, 1);
}
}
}
$truncate .= $tag[1];
$contentLength = $this->ustrlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
if ($contentLength + $totalLength > $length)
{
$left = $length - $totalLength;
$entitiesLength = 0;
if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE))
{
foreach ($entities[0] as $entity)
{
if ($entity[1] + 1 - $entitiesLength <= $left)
{
$left--;
$entitiesLength += $this->ustrlen($entity[0]);
}
else
{
break;
}
}
}
$truncate .= $this->usubstr($tag[3], 0, $left + $entitiesLength);
break;
}
$truncate .= $tag[3];
$totalLength += $contentLength;
if ($totalLength >= $length)
{
break;
}
}
if (!$exact)
{
$spacepos = $this->ustrrpos($truncate, ' ');
if (isset($spacepos))
{
$bits = $this->usubstr($truncate, $spacepos);
preg_match_all('/<\/([a-z]+)>/i', $bits, $droppedTags, PREG_SET_ORDER);
if (!empty($droppedTags))
{
foreach ($droppedTags as $closingTag)
{
if (!in_array($closingTag[1], $openTags))
{
array_unshift($openTags, $closingTag[1]);
}
}
}
$truncate = $this->usubstr($truncate, 0, $spacepos);
}
}
$truncate .= $ending;
foreach ($openTags as $tag)
{
$truncate .= '</' . $tag . '>';
}
return $truncate;
}
/**
* @param string $text string to process
* @param integer $len length of characters to be truncated
* @param string $more string which will be added if truncation
* @return string Always returns text.
* @deprecated for public use. Will be made private. Use $tp->truncate() instead.
* Truncate a string of text to a maximum length $len append the string $more if it was truncated
* Uses current CHARSET for utf-8, returns $len characters rather than $len bytes
*
*/
public function text_truncate($text, $len = 200, $more = ' ... ')
{
if ($this->ustrlen($text) <= $len)
{
return $text;
}
if ($this->isBBcode($text) || $this->isHtml($text))
{
$text = $this->toText($text);
}
$text = html_entity_decode($text, ENT_QUOTES, 'utf-8');
if (function_exists('mb_strimwidth'))
{
return mb_strimwidth($text, 0, $len, $more);
}
$ret = $this->usubstr($text, 0, $len);
// search for possible broken html entities
// - if an & is in the last 8 chars, removing it and whatever follows shouldn't hurt
// it should work for any characters encoding
$leftAmp = $this->ustrrpos($this->usubstr($ret, -8), '&');
if ($leftAmp)
{
$ret = $this->usubstr($ret, 0, $this->ustrlen($ret) - 8 + $leftAmp);
}
return $ret . $more;
}
/**
* @param $text
* @param $wrap
* @return array|string|string[]
*/
public function textclean($text, $wrap = 100)
{
$text = str_replace("\n\n\n", "\n\n", $text);
$text = $this->htmlwrap($text, $wrap);
$text = str_replace(array('<br /> ', ' <br />', ' <br /> '), '<br />', $text);
/* we can remove any linebreaks added by htmlwrap function as any \n's will be converted later anyway */
return $text;
}
/**
* Test for text highlighting, and determine the text highlighting transformation
* @return bool Returns TRUE if highlighting is active for this page display
*/
public function checkHighlighting()
{
global $pref;
if (!defined('e_SELF'))
{
// Still in startup, so can't calculate highlighting
return false;
}
if (!isset($this->e_highlighting))
{
$this->e_highlighting = false;
$shr = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
if ($pref['search_highlight'] && (strpos(e_SELF, 'search.php') === false) && ((strpos($shr, 'q=') !== false) || (strpos($shr, 'p=') !== false)))
{
$this->e_highlighting = true;
if (!isset($this->e_query))
{
preg_match('#(q|p)=(.*?)(&|$)#', $shr, $matches);
$this->e_query = str_replace(array('+', '*', '"', ' '), array('', '.*?', '', '\b|\b'), trim(urldecode($matches[2])));
}
}
}
return $this->e_highlighting;
}
/**
* Replace text represenation of website urls and email addresses with clickable equivalents.
*
* @param string $text
* @param string $type email|url
* @param array $opts options.
* $opts = [
* 'sub' => (string) substitute text within links
* 'ext' => (bool) load link in new window (not for email)
* ]
* @return string
*/
public function makeClickable($text = '', $type = 'email', $opts = array())
{
if (empty($text))
{
return '';
}
$textReplace = (!empty($opts['sub'])) ? $opts['sub'] : '';
if (substr($textReplace, -6) === '.glyph')
{
$textReplace = $this->toGlyph($textReplace, '');
}
switch ($type)
{
default:
case 'email':
preg_match_all("#(?:[\n\r ]|^)?([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i", $text, $match);
if (!empty($match[0]))
{
$srch = array();
$repl = array();
foreach ($match[0] as $eml)
{
$email = trim($eml);
$srch[] = $email;
$repl[] = $this->emailObfuscate($email, $textReplace);
}
$text = str_replace($srch, $repl, $text);
}
break;
case 'url':
$linktext = (!empty($textReplace)) ? $textReplace : '$3';
$external = (!empty($opts['ext'])) ? 'target="_blank"' : '';
$text = preg_replace("/(^|[\n \(])([\w]*?)([\w]*?:\/\/[\w]+[^ \,\"\n\r\t<]*)/is", '$1$2<a class="e-url" href="$3" ' . $external . '>' . $linktext . '</a>', $text);
$text = preg_replace("/(^|[\n \(])([\w]*?)((www)\.[^ \,\"\t\n\r\)<]*)/is", '$1$2<a class="e-url" href="http://$3" ' . $external . '>' . $linktext . '</a>', $text);
$text = preg_replace("/(^|[\n ])([\w]*?)((ftp)\.[^ \,\"\t\n\r<]*)/is", '$1$2<a class="e-url" href="$4://$3" ' . $external . '>' . $linktext . '</a>', $text);
break;
}
return $text;
}
/**
* @param string $text
* @param $postID
* @return string
*/
public function parseBBCodes($text, $postID)
{
return e107::getBB()->parseBBCodes($text, $postID);
}
/**
* Strips block tags from html.
* ie. <p> <div> <blockquote> <h1> <h2> <h3> etc are removed.
*
* @param string $html
* @return string
*/
public function stripBlockTags($html)
{
$diff = array_diff($this->allowedTags, $this->blockTags);
$parm = '';
foreach ($diff as $tag)
{
$parm .= '<' . $tag . '>';
}
return strip_tags($html, $parm);
}
/**
* @param $s
* @param $allowedattr
* @return array|mixed|string|string[]
*/
public function stripAttributes($s, $allowedattr = array())
{
if (preg_match_all("/<[^>]*\\s([^>]*)\\/*>/msiU", $s, $res, PREG_SET_ORDER))
{
foreach ($res as $r)
{
$tag = $r[0];
$attrs = array();
preg_match_all("/\\s.*=(['\"]).*\\1/msiU", " " . $r[1], $split, PREG_SET_ORDER);
foreach ($split as $spl)
{
$attrs[] = $spl[0];
}
$newattrs = array();
foreach ($attrs as $a)
{
$tmp = explode("=", $a);
if (trim($a) != "" && (!isset($tmp[1]) || (trim($tmp[0]) != "" && !in_array(strtolower(trim($tmp[0])), $allowedattr))))
{
}
else
{
$newattrs[] = $a;
}
}
$attrs = implode(" ", $newattrs);
$rpl = str_replace($r[1], $attrs, $tag);
$s = str_replace($tag, $rpl, $s);
}
}
return $s;
}
/**
* Converts the text (presumably retrieved from the database) for HTML output.
*
* @param string $text
* @param boolean $parseBB [optional]
* @param string $modifiers [optional] TITLE|SUMMARY|DESCRIPTION|BODY|RAW|LINKTEXT etc.
* Comma-separated list, no spaces allowed
* first modifier must be a CONTEXT modifier, in UPPER CASE.
* subsequent modifiers are lower case - see $this->e_Modifiers for possible values
* @param mixed $postID [optional]
* @param boolean $wrap [optional]
* @return string
* @todo complete the documentation of this essential method
*/
public function toHTML($text, $parseBB = false, $modifiers = '', $postID = '', $wrap = false)
{
if (empty($text) || !is_string($text))
{
return $text;
}
if (empty($this->pref)) // cache the prefs.
{
$prefsUsed = array('smiley_activate', 'make_clickable', 'link_replace', 'main_wordwrap', 'link_text',
'email_text', 'links_new_window', 'profanity_filter', 'tohtml_hook', 'e_tohtml_list', 'e_parse_list'
);
$cfg = e107::getConfig();
foreach ($prefsUsed as $v)
{
$this->pref[$v] = $cfg->get($v);
}
}
global $fromadmin;
// Set default modifiers to start
$opts = $this->getModifiers($modifiers);
if ($this->isHtml($text)) //BC FIx for when HTML is saved without [html][/html]
{
$opts['nobreak'] = true;
$text = trim($text);
if (strpos($text, '[center]') === 0) // quick bc fix TODO Find a better solution. [center][/center] containing HTML.
{
$text = str_replace(array('[center]', '[/center]'), array("<div style='text-align:center'>", '</div>'), $text);
}
}
$fromadmin = $opts['fromadmin'];
// Convert defines(constants) within text. eg. Lan_XXXX - must be the entire text string (i.e. not embedded)
// The check for '::' is a workaround for a bug in the Zend Optimiser 3.3.0 and PHP 5.2.4 combination
// - causes crashes if '::' in site name
if ($opts['defs'] && (strlen($text) < 35) && ((strpos($text, '::') === false) && defined(trim($text))))
{
$text = constant(trim($text)); // don't return yet, words could be hooked with linkwords etc.
}
if ($opts['no_tags'])
{
$text = strip_tags($text);
}
/*
if(MAGIC_QUOTES_GPC === true) // precaution for badly saved data.
{
$text = stripslashes($text);
}
*/
// $text = " ".$text;
// Now get on with the parsing
$ret_parser = '';
$last_bbcode = '';
// So we can change them on each loop
$saveOpts = $opts;
if ($parseBB == false)
{
$content = array($text);
}
else
{
// Split each text block into bits which are either within one of the 'key' bbcodes, or outside them
// (Because we have to match end words, the 'extra' capturing subpattern gets added to output array. We strip it later)
$content = preg_split('#(\[(table|html|php|code|scode|hide).*?\[\/(?:\\2)\])#mis', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
}
// Use $full_text variable so its available to special bbcodes if required
foreach ($content as $full_text)
{
$proc_funcs = true;
$convertNL = true;
// We may have 'captured' a bbcode word - strip it if so
if ($last_bbcode == $full_text)
{
$last_bbcode = '';
$proc_funcs = false;
$full_text = '';
}
else
{
// Set the options for this pass
$opts = $saveOpts;
// Have to have a good test in case a 'non-key' bbcode starts the block
// - so pull out the bbcode parameters while we're there
if (($parseBB !== false) && preg_match('#(^\[(table|html|php|code|scode|hide)(.*?)\])(.*?)(\[/\\2\]$)#is', $full_text, $matches))
{
$proc_funcs = false;
$full_text = '';
$code_text = $matches[4];
// $parm = $matches[3] ? substr($matches[3], 1) : '';
$last_bbcode = $matches[2];
switch ($matches[2])
{
case 'php' :
$proc_funcs = false;
$code_text = '';
break;
case 'html' : // This overrides and deprecates html.bb
$proc_funcs = true;
// $code_text = str_replace("\r\n", " ", $code_text);
// $code_text = html_entity_decode($code_text, ENT_QUOTES, CHARSET);
// $code_text = str_replace('&','&amp;',$code_text); // validation safe.
$html_start = '<!-- bbcode-html-start -->'; // markers for html-to-bbcode replacement.
$html_end = '<!-- bbcode-html-end -->';
$full_text = str_replace(array('[html]', '[/html]'), '', $code_text); // quick fix.. security issue?
$full_text = $this->parseBBCodes($full_text, $postID); // parse any embedded bbcodes eg. [img]
$full_text = $this->replaceConstants($full_text, 'abs'); // parse any other paths using {e_....
$full_text = $html_start . $full_text . $html_end;
$full_text = $this->parseBBTags($full_text); // strip <bbcode> tags.
$opts['nobreak'] = true;
$parseBB = false; // prevent further bbcode processing.
break;
case 'table' : // strip <br /> from inside of <table>
$convertNL = false;
// break;
case 'hide' :
$proc_funcs = true;
case 'scode':
case 'code' :
$full_text = $this->parseBBCodes($matches[0], $postID);
break;
}
}
}
// Do the 'normal' processing - in principle, as previously - but think about the order.
if ($proc_funcs && !empty($full_text)) // some more speed
{
// Split out and ignore any scripts and style blocks. With just two choices we can match the closing tag in the regex
$subcon = preg_split('#((?:<s)(?:cript[^>]+>.*?</script>|tyle[^>]+>.*?</style>))#mis', $full_text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($subcon as $sub_blk)
{
if (strpos($sub_blk, '<script') === 0) // Strip scripts unless permitted
{
if ($opts['scripts'])
{
$ret_parser .= html_entity_decode($sub_blk, ENT_QUOTES);
}
}
elseif (strpos($sub_blk, '<style') === 0)
{
// Its a style block - just pass it through unaltered - except, do we need the line break stuff? - QUERY XXX-01
$ret_parser .= $sub_blk;
}
else
{
// Do 'normal' processing on a chunk
// Could put tag stripping in here
/*
// Line break compression - filter white space after HTML tags - among other things, ensures HTML tables display properly
// Hopefully now achieved by other means
if ($convertNL && !$opts['nobreak'])
{
$sub_blk = preg_replace("#>\s*[\r]*\n[\r]*#", ">", $sub_blk);
}
*/
// Link substitution
// Convert URL's to clickable links, unless modifiers or prefs override
$sub_blk = $this->processModifiers($opts, $sub_blk, $convertNL, $parseBB, $modifiers, $postID);
$ret_parser .= $sub_blk;
} // End of 'normal' processing for a block of text
} // End of 'foreach() on each block of non-script text
} // End of 'normal' parsing (non-script text)
else
{
// Text block that needed no processing at all
$ret_parser .= $full_text;
}
}
// Quick Fix - Remove trailing <br /> on block-level elements (eg. div, pre, table, etc. )
$srch = array();
$repl = array();
foreach ($this->blockTags as $val)
{
$srch[] = '</' . $val . '><br />';
$repl[] = '</' . $val . '>';
}
$ret_parser = str_replace($srch, $repl, $ret_parser);
return trim($ret_parser);
}
/**
* Check if a string begins with a preformatter flag.
*
* @param $str
* @return bool
*/
private function preformatted($str)
{
foreach ($this->preformatted as $type)
{
$code = '[' . $type . ']';
if (strpos($str, $code) === 0)
{
return true;
}
}
return false;
}
/**
* @param $mixed
* @return array|false|string
*/
public function toUTF8($mixed)
{
if (is_array($mixed))
{
foreach ($mixed as $k => $v)
{
unset($mixed[$k]);
$mixed[$this->toUTF8($k)] = $this->toUTF8($v);
}
}
elseif (is_object($mixed))
{
$objVars = get_object_vars($mixed);
foreach ($objVars as $key => $value)
{
$mixed->$key = $this->toUTF8($value);
}
}
elseif (is_string($mixed))
{
return iconv('UTF-8', 'UTF-8//IGNORE', utf8_encode($mixed));
}
return $mixed;
}
/**
* @param string $text
* @return string
*/
public function toASCII($text)
{
$char_map = array(
// Latin
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C',
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O',
'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH',
'ß' => 'ss',
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c',
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o',
'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th',
'ÿ' => 'y',
// Latin symbols
'©' => '(c)',
// Greek
'Α' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E', 'Ζ' => 'Z', 'Η' => 'H', 'Θ' => '8',
'Ι' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => '3', 'Ο' => 'O', 'Π' => 'P',
'Ρ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'PS', 'Ω' => 'W',
'Ά' => 'A', 'Έ' => 'E', 'Ί' => 'I', 'Ό' => 'O', 'Ύ' => 'Y', 'Ή' => 'H', 'Ώ' => 'W', 'Ϊ' => 'I',
'Ϋ' => 'Y',
'α' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e', 'ζ' => 'z', 'η' => 'h', 'θ' => '8',
'ι' => 'i', 'κ' => 'k', 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => '3', 'ο' => 'o', 'π' => 'p',
'ρ' => 'r', 'σ' => 's', 'τ' => 't', 'υ' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'ps', 'ω' => 'w',
'ά' => 'a', 'έ' => 'e', 'ί' => 'i', 'ό' => 'o', 'ύ' => 'y', 'ή' => 'h', 'ώ' => 'w', 'ς' => 's',
'ϊ' => 'i', 'ΰ' => 'y', 'ϋ' => 'y', 'ΐ' => 'i',
// Turkish
'Ş' => 'S', 'İ' => 'I', /*'Ç' => 'C', 'Ü' => 'U', 'Ö' => 'O',*/
'Ğ' => 'G',
'ş' => 's', 'ı' => 'i', /*'ç' => 'c', 'ü' => 'u', 'ö' => 'o',*/
'ğ' => 'g',
// Russian
'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh',
'З' => 'Z', 'И' => 'I', 'Й' => 'J', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O',
'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sh', 'Ъ' => '', 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Yu',
'Я' => 'Ya',
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh',
'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',
'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sh', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 'ю' => 'yu',
'я' => 'ya',
// Ukrainian
'Є' => 'Ye', 'І' => 'I', 'Ї' => 'Yi', 'Ґ' => 'G',
'є' => 'ye', 'і' => 'i', 'ї' => 'yi', 'ґ' => 'g',
// Czech
'Č' => 'C', 'Ď' => 'D', 'Ě' => 'E', 'Ň' => 'N', 'Ř' => 'R', 'Š' => 'S', 'Ť' => 'T', 'Ů' => 'U',
'Ž' => 'Z',
'č' => 'c', 'ď' => 'd', 'ě' => 'e', 'ň' => 'n', 'ř' => 'r', 'š' => 's', 'ť' => 't', 'ů' => 'u',
'ž' => 'z',
// Polish
'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'e', 'Ł' => 'L', 'Ń' => 'N', /*'Ó' => 'o',*/
'Ś' => 'S', 'Ź' => 'Z',
'Ż' => 'Z',
'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n',/* 'ó' => 'o',*/
'ś' => 's', 'ź' => 'z',
'ż' => 'z',
// Latvian
'Ā' => 'A',/* 'Č' => 'C',*/
'Ē' => 'E', 'Ģ' => 'G', 'Ī' => 'i', 'Ķ' => 'k', 'Ļ' => 'L', 'Ņ' => 'N',
/*'Š' => 'S',*/
'Ū' => 'u',
'ā' => 'a', 'ē' => 'e', 'ģ' => 'g', 'ī' => 'i', 'ķ' => 'k', 'ļ' => 'l', 'ņ' => 'n',
'ū' => 'u',
'ľ' => 'l', 'ŕ' => 'r', 'Ľ' => 'l',
);
return str_replace(array_keys($char_map), $char_map, $text);
}
/**
* Use it on html attributes to avoid breaking markup .
*
* @param string $text
* @param bool $pure True to skip the text mutation by {@see e_parse::replaceConstants()}
* @example echo "<a href='#' title='".$tp->toAttribute($text)."'>Hello</a>";
*/
public function toAttribute($text, $pure = false)
{
// URLs posted without HTML access may have an &amp; in them.
// Xhtml compliance.
$text = htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8');
if (!$pure && !preg_match('/&#|\'|"|<|>/s', $text))
{
$text = $this->replaceConstants($text);
}
return $text;
}
/**
* Build a series of HTML attributes from the provided array
*
* Because of legacy loose typing client code usages, values that are {@see empty()} will not be added to the
* concatenated HTML attribute string except when the key is `value`, the key begins with `data-`, or the value is
* a number.
*
* @param array $attributes Key-value pairs of HTML attributes. The value must not be HTML-encoded. If the value is
* boolean true, the value will be set to the key (e.g. `['required' => true]` becomes
* "required='required'").
* @param bool $pure True to skip the text mutation by {@see e_parse::replaceConstants()}
* @return string The HTML attributes to concatenate inside an HTML tag
* @see e_parseTest::testToAttributesMixedPureAndReplaceConstants() for an example of how to use this method
*/
public function toAttributes($attributes, $pure = false)
{
$stringifiedAttributes = [];
foreach ($attributes as $key => $value)
{
if ($value === true && (strpos($key, 'data-') !== 0))
{
$value = $key;
}
if (!empty($value) || is_numeric($value) || $key === "value" || strpos($key, 'data-') === 0)
{
$stringifiedAttributes[] = $key . "='" . $this->toAttribute($value, $pure) . "'";
}
}
return count($stringifiedAttributes) > 0 ? " " . implode(" ", $stringifiedAttributes) : "";
}
/**
* Flatten a multi-dimensional associative array with slashes.
*
* Based on Illuminate\Support\Arr::dot()
*
* @param $array
* @param string $prepend
* @return array
* @license https://github.com/illuminate/support/blob/master/LICENSE.md MIT License
* @copyright Copyright (c) Taylor Otwell
*/
public static function toFlatArray($array, $prepend = '')
{
$results = [];
foreach ($array as $key => $value)
{
if (is_array($value) && !empty($value))
{
$results = array_merge($results, static::toFlatArray($value, $prepend . $key . '/'));
}
else
{
$results[$prepend . $key] = $value;
}
}
return $results;
}
/**
* Convert a flattened slash-delimited multi-dimensional array back into an actual multi-dimensional array
*
* Inverse of {@link e_parse::toFlatArray()}
*
* @param $array
* @param string $unprepend
* @return array
*/
public static function fromFlatArray($array, $unprepend = '')
{
$output = [];
foreach ($array as $key => $value)
{
if (!empty($unprepend) && substr($key, 0, strlen($unprepend)) == $unprepend)
{
$key = substr($key, strlen($unprepend));
}
$parts = explode('/', $key);
$nested = &$output;
while (count($parts) > 1)
{
$nested = &$nested[array_shift($parts)];
if (!is_array($nested)) $nested = [];
}
$nested[array_shift($parts)] = $value;
}
return $output;
}
/**
* Convert text blocks which are to be embedded within JS
*
* @param string|array $stringarray
* @return string
* @deprecated v2.3.1 This method will not escape a string properly for use as a JavaScript or JSON string. Use
* {@see e_parse::toJSON()} instead. When using {@see e_parse::toJSON()}, do not surround its output
* with quotation marks, and do not attempt to escape sequences like "\n" as "\\n". If HTML tags need to
* be removed, consider {@see e_parse::toText()} separately. If the text needs to be used in an HTML
* tag attribute (e.g. &lt;a onclick="ATTRIBUTE"&gt;&lt;/a&gt;), surround the string with
* {@see e_parse::toAttribute()} and either single-quote or double-quote the attribute value.
*/
public function toJS($stringarray)
{
trigger_error('<b>' . __METHOD__ . ' is deprecated. See method DocBlock for alternatives.</b>', E_USER_WARNING); // NO LAN
$search = array("\r\n", "\r", '<br />', "'");
$replace = array("\\n", '', "\\n", "\'");
$stringarray = str_replace($search, $replace, $stringarray);
$stringarray = strip_tags($stringarray);
$trans_tbl = get_html_translation_table(HTML_ENTITIES);
$trans_tbl = array_flip($trans_tbl);
return strtr($stringarray, $trans_tbl);
}
/**
* Converts a PHP variable into its JavaScript equivalent.
* We use HTML-safe strings, with several characters escaped.
*
* @param mixed $var
* PHP variable.
* @param bool $force_object
* True: Outputs an object rather than an array when a non-associative
* array is used. Especially useful when the recipient of the output
* is expecting an object and the array is empty.
*
* @return string
*/
public function toJSON($var, $force_object = false)
{
if ($force_object === true)
{
// Encode <, >, ', &, and " using the json_encode() options parameter.
return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_FORCE_OBJECT);
}
// Encode <, >, ', &, and " using the json_encode() options parameter.
return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
}
/**
* Convert Text for RSS/XML use.
*
* @param string $text
* @param boolean $tags [optional]
* @return string
*/
public function toRss($text, $tags = false)
{
if ($tags != true)
{
$text = $this->toHTML($text, true);
$text = strip_tags($text);
}
$text = $this->toEmail($text);
$search = array('&amp;#039;', '&amp;#036;', '&#039;', '&#036;', e_BASE, "href='request.php", '<!-- bbcode-html-start -->', '<!-- bbcode-html-end -->');
$replace = array("'", '$', "'", '$', SITEURL, "href='" . SITEURL . 'request.php', '', '');
$text = str_replace($search, $replace, $text);
$text = $this->ampEncode($text);
// if CDATA happens to be quoted in the text.
$text = str_replace(['<![CDATA', ']]>'], ['&lt;![CDATA', ']]&gt;'], $text);
if ($tags === true)
{
$text = !empty($text) ? '<![CDATA[' . $text . ']]>' : '';
}
else
{
$text = str_replace(['<', '>'], ['&lt;', '&gt;'], $text);
}
return $text;
}
/**
* Convert a string to a number (int/float)
*
* @param string $value
* @return int|float
*/
public function toNumber($value)
{
// adapted from: https://secure.php.net/manual/en/function.floatval.php#114486
$dotPos = strrpos($value, '.');
$commaPos = strrpos($value, ',');
$sep = (($dotPos > $commaPos) && $dotPos) ? $dotPos :
((($commaPos > $dotPos) && $commaPos) ? $commaPos : false);
if (!$sep)
{
return (int) preg_replace('/[^-0-9]/', '', $value);
}
return (float) (
preg_replace('/[^-0-9]/', '', substr($value, 0, $sep)) . '.' .
preg_replace('/[^0-9]/', '', substr($value, $sep + 1, strlen($value)))
);
}
/**
* Clean and Encode Ampersands '&' for output to browser.
*
* @param string $text
* @return array|string|string[]
*/
public function ampEncode($text = '')
{
// Fix any left-over '&'
//first revert any previously converted.
$text = str_replace(array('&amp;', '&'), array('&', '&amp;'), $text);
return $text;
}
/**
* Convert any string back to plain text.
*
* @param $text
* @return array|string|string[]
*/
public function toText($text)
{
$text = (string) $text;
if ($this->isBBcode($text) === true) // convert any bbcodes to html
{
$text = $this->toHTML($text, true);
}
if ($this->isHtml($text) === true) // strip any html.
{
$text = $this->toHTML($text, true);
$text = str_replace("\n", '', $text); // clean-out line-breaks.
$text = str_ireplace(array('<br>', '<br />', '<br/>'), "\n", $text);
$text = strip_tags($text);
}
$search = array('&amp;#039;', '&amp;#036;', '&#039;', '&#036;', '&#092;', '&amp;#092;');
$replace = array("'", '$', "'", '$', "\\", "\\");
$text = str_replace($search, $replace, $text);
return $text;
}
/**
* Set the dimensions of a thumbNail (generated by thumbUrl)
*/
public function setThumbSize($w = null, $h = null, $crop = null)
{
if ($w !== null)
{
$this->thumbWidth = (int) $w;
}
if ($h !== null)
{
$this->thumbHeight = (int) $h;
}
if ($crop !== null)
{
$this->thumbCrop = (int) $crop;
}
}
/**
* @param $val
* @return int|null
*/
public function thumbEncode($val = null)
{
if ($val !== null)
{
$this->thumbEncode = (int) $val;
return null;
}
return $this->thumbEncode;
}
/**
* Retrieve img tag width and height attributes for current thumbnail.
*
* @return string
*/
public function thumbDimensions($type = 'single')
{
if (!empty($this->thumbCrop) && !empty($this->thumbWidth) && !empty($this->thumbHeight)) // dimensions are known.
{
return ($type === 'double') ? 'width="' . $this->thumbWidth . '" height="' . $this->thumbHeight . '"' : "width='" . $this->thumbWidth . "' height='" . $this->thumbHeight . "'";
}
return null;
}
/**
* Set or Get the value of the thumbNail Width.
*
* @param $width (optional)
*/
public function thumbWidth($width = null)
{
if ($width !== null)
{
$this->thumbWidth = (int) $width;
}
return $this->thumbWidth;
}
/**
* Set or Get the value of the thumbNailbCrop.
*
* @param bool $status = true/false
*/
public function thumbCrop($status = false)
{
if ($status !== false)
{
$this->thumbCrop = (int) $status;
}
return $this->thumbCrop;
}
/**
* Set or Get the value of the thumbNail height.
*
* @param $height (optional)
*/
public function thumbHeight($height = null)
{
if ($height !== null)
{
$this->thumbHeight = (int) $height;
}
return $this->thumbHeight;
}
/**
* Sets a cache file for storing alternate text data for an image.
*
* @param string $path The file path of the image whose alternate text is being cached.
* @param string $value The alternate text value to cache.
* @return string or false on failure
*/
public function setImageAltCacheFile($path, $value)
{
$cache_str = sha1($path);
$pre = 'alt_';
$post = '.cache.txt';
$file = e_CACHE_IMAGE .'alt_' . sha1($path) . '.cache.txt';
if (file_put_contents($file, $value) !== false)
{
return $file;
}
return false;
}
/**
* Retrieves the content of the alternative cache file for the specified path if it exists.
*
* @param string $path The file path to generate the cache filename for.
* @return string|null The content of the cache file if it exists, or null if the file does not exist.
*/
public function getImageAltCacheFile($path)
{
$file = e_CACHE_IMAGE .'alt_' . sha1($path) . '.cache.txt';
if (file_exists($file))
{
return file_get_contents($file);
}
return null;
}
/**
* Generated a Thumb Cache File Name from path and options.
*
* @param string $path
* @param array $options
* @param string $log (optional) - log file name
* @return null|string
*/
public function thumbCacheFile($path, $options = null, $log = null)
{
if (empty($path))
{
return null;
}
if (is_string($options))
{
parse_str($options, $options);
}
$path = str_replace($this->getUrlConstants('raw'), $this->getUrlConstants('sc'), $path);
$path = $this->replaceConstants(str_replace('..', '', $path));
$filename = basename($path);
$tmp = explode('.', $filename);
$ext = end($tmp);
$len = strlen($ext) + 1;
$start = substr($filename, 0, -$len);
// cleanup.
$newOpts = array(
'w' => isset($options['w']) ? (string) intval($options['w']) : '',
'h' => isset($options['h']) ? (string) intval($options['h']) : '',
'aw' => isset($options['aw']) ? (string) intval($options['aw']) : '',
'ah' => isset($options['ah']) ? (string) intval($options['ah']) : '',
'c' => strtoupper(vartrue($options['c'], '0')),
);
if (!empty($options['type']))
{
$newOpts['type'] = $options['type'];
$ext = $newOpts['type'];
}
if (!empty($options['aw']))
{
$options['w'] = $options['aw'];
}
if (!empty($options['ah']))
{
$options['h'] = $options['ah'];
}
$size = varset($options['w'], 0) . 'x' . varset($options['h'], 0);
$thumbQuality = e107::getPref('thumbnail_quality', 65);
$cache_str = md5(serialize($newOpts) . $path . $thumbQuality);
$pre = 'thumb_';
$post = '.cache.bin';
// $cache_str = http_build_query($newOpts,null,'_'); // testing files.
if (defined('e_MEDIA_STATIC')) // experimental - subject to change.
{
$pre = '';
$post = '';
}
$fname = $pre . strtolower($start . '_' . $cache_str . '_' . $size . '.' . $ext) . $post;
if ($log !== null)
{
file_put_contents(e_LOG . $log, "\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n", FILE_APPEND);
$message = 'Name: ' . $fname . "\n";
$message .= $path . "\n" . var_export($newOpts, true) . "\n\n\n";
file_put_contents(e_LOG . $log, $message, FILE_APPEND);
// file_put_contents(e_LOG.$log, "\t\tFOUND!!\n\n\n", FILE_APPEND);
}
return $fname;
}
/**
* @param bool|int $val
* @return int
*/
private function staticCount($val = false)
{
$count = $this->staticCount;
if ($val === 0)
{
$this->staticCount = 0;
}
elseif ($val !== false)
{
$this->staticCount += (int) $val;
}
return $count;
}
/**
* @param string $path - absolute path or e107 path {e_PLUGIN} etc.
* @param array $opts - when $opts['full'] is true, a full siteurl will be used instead of an absolute path. (unless static url is found)
* @return string - full path or static path.
* @todo Move to e107_class ?
*/
public function staticUrl($path = null, $opts = array())
{
if (empty($this->staticUrl) || deftrue('e_ADMIN_AREA'))
{
// e107::getDebug()->log("e_HTTP_STATIC not defined");
if ($path === null)
{
return !empty($opts['full']) ? SITEURL : e_HTTP;
}
return !empty($opts['full']) ? $this->replaceConstants($path, 'full') : $this->replaceConstants($path, 'abs'); // self::replaceConstants($path, 'full');
}
$staticArray = $this->staticUrl; // e_HTTP_STATIC;
$path = $this->replaceConstants($path, 'abs'); // replace any {THEME} etc.
$key = ltrim(eHelper::dasherize($path), '/');
if (is_array($staticArray))
{
if(!empty($this->staticUrlMap[$key]))
{
$http = $this->staticUrlMap[$key];
}
else
{
$cnt = count($staticArray);
$staticCount = $this->staticCount();
if ($staticCount > ($cnt - 1))
{
$staticCount = 0;
$this->staticCount(0);
}
$http = !empty($staticArray[$staticCount]) ? $staticArray[$staticCount] : e_HTTP;
$this->staticCount(1);
}
}
else
{
$http = $this->staticUrl;
}
$this->staticUrlMap[$key] = $http;
if (empty($path))
{
return $http;
}
$base = '';
$srch = array(
e_PLUGIN_ABS,
e_THEME_ABS,
e_WEB_ABS,
e_CACHE_IMAGE_ABS,
);
$repl = array(
$http . $base . e107::getFolder('plugins'),
$http . $base . e107::getFolder('themes'),
$http . $base . e107::getFolder('web'),
$http . $base . str_replace('../', '', e_CACHE_IMAGE),
);
// Quickfix for any '/./' that may occur.
$srch[] = '/./';
$repl[] = '/';
$ret = str_replace($srch, $repl, $path);
if (strpos($ret, 'http') !== 0) // if not converted, check media folder also.
{
$ret = str_replace(e_MEDIA_ABS, $http . $base . e107::getFolder('media'), $ret);
}
return $ret;
}
/**
* Used internally to store e_HTTP_STATIC.
*
* @param string|null $url The static URL ie. e_HTTP_STATIC
*/
public function setStaticUrl($url)
{
$this->staticUrl = $url;
}
public function getStaticUrlMap()
{
return $this->staticUrlMap;
}
/**
* Generate an auto-sized Image URL.
*
* @param $url - path to image or leave blank for a placeholder. eg. {e_MEDIA}folder/my-image.jpg
* @param array $options = [ width and height, but leaving this empty and using $this->thumbWidth() and $this->thumbHeight() is preferred. ie. {SETWIDTH: w=x&y=x}
* 'w' => int width (optional)
* 'h' => int height (optional)
* 'crop' => bool|string true/false or A(auto) or T(op) or B(ottom) or C(enter) or L(eft) or R(right)
* 'scale' => string '2x' (optional)
* 'x' => bool encode/mask the url parms (optional)
* 'nosef' => bool when set to true disabled SEF Url being returned (optional)
* ]
* @param bool $raw set to true when the $url does not being with an e107 variable ie. "{e_XXXX}" eg. {e_MEDIA} (optional)
* @param bool $full when true returns full http:// url. (optional)
* ]
* @return string
*/
public function thumbUrl($url = null, $options = array(), $raw = false, $full = false)
{
$url = (string) $url;
$this->staticCount++; // increment counter.
$ext = pathinfo($url, PATHINFO_EXTENSION);
if ($ext === 'svg')
{
return $this->replaceConstants($url, 'abs');
}
if (strpos($url, '{e_') === 0) // Fix for broken links that use {e_MEDIA} etc.
{
//$url = $this->replaceConstants($url,'abs');
// always switch to 'nice' urls when SC is used
$url = str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url);
}
if (is_string($options))
{
parse_str($options, $options);
}
if (!empty($options['scale'])) // eg. scale the width height 2x 3x 4x. etc.
{
$options['return'] = 'src';
$options['size'] = $options['scale'];
unset($options['scale']);
return $this->thumbSrcSet($url, $options);
}
if (strpos($url, e_MEDIA) !== false || strpos($url, e_SYSTEM) !== false) // prevent disclosure of 'hashed' path.
{
$raw = true;
}
if ($raw)
{
$url = $this->createConstants($url, 'mix');
}
$baseurl = ($full ? SITEURL : e_HTTP) . 'thumb.php?';
if (!empty($this->staticUrl))
{
$baseurl = $this->staticUrl() . 'thumb.php?';
}
$thurl = 'src=' . urlencode($url) . '&amp;';
// e107::getDebug()->log("Thumb: ".basename($url). print_a($options,true), E107_DBG_BASIC);
if (!empty($options) && (isset($options['w']) || isset($options['aw']) || isset($options['h'])))
{
$options['w'] = varset($options['w']);
$options['h'] = varset($options['h']);
$options['crop'] = (isset($options['aw']) || isset($options['ah'])) ? 1 : varset($options['crop']);
$options['aw'] = varset($options['aw']);
$options['ah'] = varset($options['ah']);
$options['x'] = varset($options['x']);
}
else
{
$options['w'] = $this->thumbWidth;
$options['h'] = $this->thumbHeight;
$options['crop'] = $this->thumbCrop;
$options['aw'] = null;
$options['ah'] = null;
$options['x'] = $this->thumbEncode;
}
if (!empty($options['crop']))
{
if (!empty($options['aw']) || !empty($options['ah']))
{
$options['w'] = $options['aw'];
$options['h'] = $options['ah'];
}
$thurl .= 'aw=' . (int) $options['w'] . '&amp;ah=' . (int) $options['h'];
if (!is_numeric($options['crop']))
{
$thurl .= '&amp;c=' . $options['crop'];
$options['nosef'] = true;
}
}
else
{
$thurl .= 'w=' . (int) $options['w'] . '&amp;h=' . (int) $options['h'];
}
if (!empty($options['type']))
{
$thurl .= '&amp;type=' . $options['type'];
}
if (defined('e_MEDIA_STATIC')) // experimental - subject to change.
{
$opts = str_replace('&amp;', '&', $thurl);
$staticFile = $this->thumbCacheFile($url, $opts);
if (!empty($staticFile) && is_readable(e_CACHE_IMAGE . $staticFile))
{
return $this->staticUrl(e_CACHE_IMAGE_ABS . $staticFile);
}
// echo "<br />static-not-found: ".$staticFile;
$options['nosef'] = true;
$options['x'] = null;
// file_put_contents(e_LOG."thumb.log", "\n++++++++++++++++++++++++++++++++++\n\n", FILE_APPEND);
}
if ($this->modRewriteMedia && empty($options['nosef']))// SEF URL support.
{
$options['full'] = $full;
$options['ext'] = pathinfo($url, PATHINFO_EXTENSION);
$options['thurl'] = $thurl;
// $options['x'] = $this->thumbEncode();
if ($sefUrl = $this->thumbUrlSEF($url, $options))
{
return $sefUrl;
}
}
if (!empty($options['x']))//base64 encode url
{
$thurl = 'id=' . base64_encode($thurl);
}
return $baseurl . $thurl;
}
/**
* Split a thumb.php url into an array which can be parsed back into the thumbUrl method. .
*
* @param $src
* @return array
*/
public function thumbUrlDecode($src)
{
list($url, $qry) = array_pad(explode('?', $src), 2, null);
$ret = array();
if (!empty($qry) && strpos($url, 'thumb.php') !== false) // Regular
{
parse_str($qry, $val);
$ret = $val;
}
elseif (preg_match('/media\/img\/(a)?([\d]*)x(a)?([\d]*)\/(.*)/', $url, $match)) // SEF
{
$wKey = $match[1] . 'w';
$hKey = $match[3] . 'h';
$ret = array(
'src' => 'e_MEDIA_IMAGE/' . $match[5],
$wKey => $match[2],
$hKey => $match[4]
);
}
elseif (preg_match('/theme\/img\/(a)?([\d]*)x(a)?([\d]*)\/(.*)/', $url, $match)) // Theme-image SEF Urls
{
$wKey = $match[1] . 'w';
$hKey = $match[3] . 'h';
$ret = array(
'src' => 'e_THEME/' . $match[5],
$wKey => $match[2],
$hKey => $match[4]
);
}
elseif (defined('TINYMCE_DEBUG'))
{
print_a('thumbUrlDecode: No Matches');
}
return $ret;
}
/**
* Experimental: Generate a Thumb URL for use in the img srcset attribute.
*
* @param string $src eg. {e_MEDIA_IMAGE}myimage.jpg
* @param int|string|array $width - desired size in px or '2x' or '3x' or null for all or array (
* @return string
*/
public function thumbSrcSet($src = '', $width = null)
{
$multiply = null;
$encode = false;
$parm = array();
if (is_array($width))
{
$parm = $width;
$multiply = $width['size'];
$encode = (!empty($width['x'])) ? $width['x'] : false;
$width = $width['size'];
}
// $encode = $this->thumbEncode();;
if ($width == null || $width === 'all')
{
$links = array();
$mag = ($width == null) ? array(1, 2) : array(160, 320, 460, 600, 780, 920, 1100);
foreach ($mag as $v)
{
$w = ($this->thumbWidth * $v);
$h = ($this->thumbHeight * $v);
$att = (!empty($this->thumbCrop)) ? array('aw' => $w, 'ah' => $h) : compact('w', 'h');
$att['x'] = $encode;
$add = ($width == null) ? ' ' . $v . 'x' : ' ' . $v . 'w';
$links[] = $this->thumbUrl($src, $att) . $add; // " w".$width; //
}
return implode(', ', $links);
}
elseif ($multiply === '2x' || $multiply === '3x' || $multiply === '4x')
{
$multiInt = (int) $multiply;
if (empty($parm['w']) && isset($parm['h']))
{
$parm['h'] = ($parm['h'] * $multiInt);
return $this->thumbUrl($src, $parm) . ' ' . $multiply;
}
if (isset($parm['w']) && !isset($parm['h'])) // if w set, assume h value of 0 is set.
{
$parm['h'] = 0;
}
$width = !empty($parm['w']) ? (intval($parm['w']) * $multiInt) : ($this->thumbWidth * $multiInt);
$height = isset($parm['h']) ? (intval($parm['h']) * $multiInt) : ($this->thumbHeight * $multiInt);
}
else
{
$height = (($this->thumbHeight * $width) / $this->thumbWidth);
}
if (!isset($parm['aw']))
{
$parm['aw'] = null;
}
if (!isset($parm['ah']))
{
$parm['ah'] = null;
}
if (!isset($parm['x']))
{
$parm['x'] = null;
}
if (!isset($parm['crop']))
{
$parm['crop'] = null;
}
$parms = array('w' => $width, 'h' => $height, 'crop' => $parm['crop'], 'x' => $parm['x'], 'aw' => $parm['aw'], 'ah' => $parm['ah']);
if (!empty($parm['type']))
{
$parms['type'] = $parm['type'];
}
// $parms = !empty($this->thumbCrop) ? array('aw' => $width, 'ah' => $height, 'x'=>$encode) : array('w' => $width, 'h' => $height, 'x'=>$encode );
// $parms['x'] = $encode;
if (!empty($parm['return']) && $parm['return'] === 'src')
{
return $this->thumbUrl($src, $parms);
}
$ret = $this->thumbUrl($src, $parms);
$ret .= ($multiply) ? ' ' . $multiply : ' ' . $width . 'w';
return $ret;
}
/**
* Used by thumbUrl when SEF Image URLS is active. @param $url
*
* @param array $options
* @return string
* @see e107.htaccess
*/
public function thumbUrlSEF($url = '', $options = array())
{
if (!empty($options['full']))
{
$base = SITEURL;
}
else
{
$base = (!empty($options['ebase'])) ? '{e_BASE}' : e_HTTP;
}
if (!empty($this->staticUrl))
{
$base = $this->staticUrl();
}
// $base = (!empty($options['full'])) ? SITEURL : e_HTTP;
if (!empty($options['x']) && !empty($options['ext'])) // base64 encoded. Build URL for: RewriteRule ^media\/img\/([-A-Za-z0-9+/]*={0,3})\.(jpg|gif|png)?$ thumb.php?id=$1
{
$ext = strtolower($options['ext']);
return $base . 'media/img/' . base64_encode($options['thurl']) . '.' . str_replace('jpeg', 'jpg', $ext);
}
elseif (strpos($url, 'e_MEDIA_IMAGE') !== false) // media images.
{
$sefPath = 'media/img/';
$clean = array('{e_MEDIA_IMAGE}', 'e_MEDIA_IMAGE/');
}
elseif (strpos($url, 'e_AVATAR') !== false) // avatars
{
$sefPath = 'media/avatar/';
$clean = array('{e_AVATAR}', 'e_AVATAR/');
}
elseif (strpos($url, 'e_THEME') !== false) // theme folder images.
{
$sefPath = 'theme/img/';
$clean = array('{e_THEME}', 'e_THEME/');
}
else
{
// e107::getDebug()->log("SEF URL False: ".$url);
return false;
}
// Build URL for ReWriteRule ^media\/img\/(a)?([\d]*)x(a)?([\d]*)\/(.*)?$ thumb.php?src=e_MEDIA_IMAGE/$5&$1w=$2&$3h=$4
$sefUrl = $base . $sefPath;
if (!empty($options['scale']))
{
$multiInt = (int) $options['scale'];
$options['w'] = $options['w'] * $multiInt;
$options['h'] = $options['h'] * $multiInt;
}
if (!empty($options['aw']) || !empty($options['ah']))
{
$sefUrl .= 'a' . intval($options['aw']) . 'xa' . intval($options['ah']);
}
elseif (!empty($options['crop']))
{
if (!is_numeric($options['crop']))
{
$sefUrl .= strtolower($options['crop']) . intval($options['w']) . 'x' . strtolower($options['crop']) . intval($options['h']);
}
else
{
$sefUrl .= 'a' . intval($options['w']) . 'xa' . intval($options['h']);
}
}
else
{
$sefUrl .= intval($options['w']) . 'x' . intval($options['h']);
}
$sefUrl .= '/';
$sefUrl .= str_replace($clean, '', $url);
return $sefUrl;
}
/**
* Help for converting to more safe URLs
* e.g. {e_MEDIA_FILE}path/to/video.flv => e_MEDIA_FILE/path/to/video.flv
*
* @param string $type sc|raw|rev|all
* @return array
* @todo support for ALL URL shortcodes (replacement methods)
*/
public function getUrlConstants($type = 'sc')
{
// sub-folders first!
static $array = array(
'e_MEDIA_FILE/' => '{e_MEDIA_FILE}',
'e_MEDIA_VIDEO/' => '{e_MEDIA_VIDEO}',
'e_MEDIA_IMAGE/' => '{e_MEDIA_IMAGE}',
'e_MEDIA_ICON/' => '{e_MEDIA_ICON}',
'e_AVATAR/' => '{e_AVATAR}',
'e_AVATAR_DEFAULT/' => '{e_AVATAR_DEFAULT}',
'e_AVATAR_UPLOAD/' => '{e_AVATAR_UPLOAD}',
'e_WEB_JS/' => '{e_WEB_JS}',
'e_WEB_CSS/' => '{e_WEB_CSS}',
'e_WEB_IMAGE/' => '{e_WEB_IMAGE}',
'e_IMPORT/' => '{e_IMPORT}',
// 'e_WEB_PACK/' => '{e_WEB_PACK}',
'e_BASE/' => '{e_BASE}',
'e_ADMIN/' => '{e_ADMIN}',
'e_IMAGE/' => '{e_IMAGE}',
'e_THEME/' => '{e_THEME}',
'e_PLUGIN/' => '{e_PLUGIN}',
'e_HANDLER/' => '{e_HANDLER}', // BC
'e_MEDIA/' => '{e_MEDIA}',
'e_WEB/' => '{e_ADMIN}',
// 'THEME/' => '{THEME}',
);
switch ($type)
{
case 'sc':
return array_values($array);
break;
case 'raw':
return array_keys($array);
break;
case 'rev':
return array_reverse($array, true);
break;
case 'all':
return $array;
break;
}
return array();
}
/**
* @return array
*/
public function getEmotes()
{
return e107::getEmote()->getList();
}
/**
* Replace e107 path constants
* Note: only an ADMIN user can convert {e_ADMIN}
* TODO - runtime cache of search/replace arrays (object property) when $mode !== ''
*
* @param string $text
* @param string $mode [optional] abs|full "full" = produce absolute URL path, e.g. http://sitename.com/e107_plugins/etc
* 'abs' = produce truncated URL path, e.g. e107plugins/etc
* "" (default) = URL's get relative path e.g. ../e107_plugins/etc
* @param mixed $all [optional] if TRUE, then when $mode is "full" or TRUE, USERID is also replaced...
* when $mode is "" (default), ALL other e107 constants are replaced
* @return string|array
*/
public function replaceConstants($text, $mode = '', $all = false)
{
if (is_array($text))
{
$new = array();
foreach ($text as $k => $v)
{
$new[$k] = $this->replaceConstants($v, $mode, $all);
}
return $new;
}
$replace_absolute = array();
if (!empty($mode))
{
$e107 = e107::getInstance();
if (empty($this->relativePaths)) // prevent multiple lookups.
{
$this->relativePaths = array(
$e107::getFolder('media_files'),
$e107::getFolder('media_video'),
$e107::getFolder('media_image'),
$e107::getFolder('media_icon'),
$e107::getFolder('avatars'),
$e107::getFolder('web_js'),
$e107::getFolder('web_css'),
$e107::getFolder('web_image'),
//$e107->getFolder('web_pack'),
e_IMAGE_ABS,
e_THEME_ABS,
$e107::getFolder('images'),
$e107::getFolder('plugins'),
$e107::getFolder('files'),
$e107::getFolder('themes'),
// $e107->getFolder('downloads'),
$e107::getFolder('handlers'),
$e107::getFolder('media'),
$e107::getFolder('web'),
$e107->site_theme ? $e107::getFolder('themes') . $e107->site_theme . '/' : '',
defset('THEME_ABS'),
(deftrue('ADMIN') ? $e107::getFolder('admin') : ''),
'',
$e107::getFolder('core'),
$e107::getFolder('system'),
);
}
$replace_relative = $this->relativePaths;
switch ($mode)
{
case 'abs':
$replace_absolute = array(
e_MEDIA_FILE_ABS,
e_MEDIA_VIDEO_ABS,
e_MEDIA_IMAGE_ABS,
e_MEDIA_ICON_ABS,
e_AVATAR_ABS,
e_JS_ABS,
e_CSS_ABS,
e_WEB_IMAGE_ABS,
// e_PACK_ABS,
e_IMAGE_ABS,
e_THEME_ABS,
e_IMAGE_ABS,
e_PLUGIN_ABS,
e_FILE_ABS,
e_THEME_ABS,
// e_DOWNLOAD_ABS, //impossible when download is done via php.
'', // handlers - no ABS path available
e_MEDIA_ABS,
e_WEB_ABS,
defset('THEME_ABS'),
defset('THEME_ABS'),
(ADMIN ? e_ADMIN_ABS : ''),
$e107->server_path,
'', // no e_CORE absolute path
'', // no e_SYSTEM absolute path
);
break;
case 'full':
$replace_absolute = array(
SITEURLBASE . e_MEDIA_FILE_ABS,
SITEURLBASE . e_MEDIA_VIDEO_ABS,
SITEURLBASE . e_MEDIA_IMAGE_ABS,
SITEURLBASE . e_MEDIA_ICON_ABS,
SITEURLBASE . e_AVATAR_ABS,
SITEURLBASE . e_JS_ABS,
SITEURLBASE . e_CSS_ABS,
SITEURLBASE . e_WEB_IMAGE_ABS,
// SITEURLBASE.e_PACK_ABS,
SITEURLBASE . e_IMAGE_ABS,
SITEURLBASE . e_THEME_ABS,
SITEURLBASE . e_IMAGE_ABS,
SITEURLBASE . e_PLUGIN_ABS,
SITEURLBASE . e_FILE_ABS, // deprecated
SITEURLBASE . e_THEME_ABS,
//SITEURL.$e107->getFolder('downloads'),
'', // handlers - no ABS path available
SITEURLBASE . e_MEDIA_ABS,
SITEURLBASE . e_WEB_ABS,
defset('THEME_ABS') ? SITEURLBASE . THEME_ABS : '',
defset('THEME_ABS') ? SITEURLBASE . THEME_ABS : '',
(deftrue('ADMIN') ? SITEURLBASE . e_ADMIN_ABS : ''),
SITEURL,
'', // no e_CORE absolute path
'', // no e_SYSTEM absolute path
);
break;
}
// sub-folders first!
$search = array(
'{e_MEDIA_FILE}',
'{e_MEDIA_VIDEO}',
'{e_MEDIA_IMAGE}',
'{e_MEDIA_ICON}',
'{e_AVATAR}',
'{e_WEB_JS}',
'{e_WEB_CSS}',
'{e_WEB_IMAGE}',
// '{e_WEB_PACK}',
'{e_IMAGE_ABS}',
'{e_THEME_ABS}',
'{e_IMAGE}',
'{e_PLUGIN}',
'{e_FILE}',
'{e_THEME}',
//,"{e_DOWNLOAD}"
'{e_HANDLER}',
'{e_MEDIA}',
'{e_WEB}',
'{THEME}',
'{THEME_ABS}',
'{e_ADMIN}',
'{e_BASE}',
'{e_CORE}',
'{e_SYSTEM}',
);
/*if (ADMIN)
{
$replace_relative[] = $e107->getFolder('admin');
$replace_absolute[] = SITEURL.$e107->getFolder('admin');
$search[] = "{e_ADMIN}";
}*/
if ($all)
{
if (USER)
{ // Can only replace with valid number for logged in users
$replace_relative[] = USERID;
$replace_absolute[] = USERID;
}
else
{
$replace_relative[] = '';
$replace_absolute[] = '';
}
$search[] = '{USERID}';
}
// current THEME
/*if(!defined('THEME'))
{
//if not already parsed by doReplace
$text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text);
}
else
{
$replace_relative[] = THEME;
$replace_absolute[] = THEME_ABS;
$search[] = "{THEME}";
$replace_relative[] = THEME;
$replace_absolute[] = THEME_ABS;
$search[] = "{THEME_ABS}";
}*/
$replace = ((string) $mode === 'full' || (string) $mode === 'abs') ? $replace_absolute : $replace_relative;
return str_replace($search, $replace, $text);
}
// $pattern = ($all ? "#\{([A-Za-z_0-9]*)\}#s" : "#\{(e_[A-Z]*)\}#s");
$pattern = ($all ? '#\{([A-Za-z_0-9]*)\}#s' : '#\{(e_[A-Z]*(?:_IMAGE|_VIDEO|_FILE|_CONTENT|_ICON|_AVATAR|_JS|_CSS|_PACK|_DB|_ABS){0,1})\}#s');
$text = preg_replace_callback($pattern, array($this, 'doReplace'), $text);
if (!defined('THEME'))
{
//if not already parsed by doReplace
$text = str_replace(array('{THEME}', '{THEME_ABS}'), '', $text);
}
else
{
$srch = array('{THEME}', '{THEME_ABS}');
$repl = array(THEME, THEME_ABS);
$text = str_replace($srch, $repl, $text);
}
return $text;
}
/**
* @param array $matches
* @return mixed|string
*/
private function doReplace($matches)
{
if (defined($matches[1]) && (deftrue('ADMIN') || strpos($matches[1], 'ADMIN') === false))
{
return constant($matches[1]);
}
return $matches[1];
}
/**
* Create and substitute e107 constants in passed URL
*
* @param string $url
* @param integer $mode 0-folders, 1-relative ('rel'), 2-absolute ('abs'), 3-full ('full') (with domain), 4-absolute & relative ('mix') (combination of 1,2,3)
* @return string
*/
public function createConstants($url, $mode = 0)
{
//FIXME - create constants for absolute paths and site URL's
if (!is_numeric($mode))
{
switch ($mode)
{
case 'rel' :
$mode = 1;
break;
case 'abs' :
$mode = 2;
break;
case 'full' :
$mode = 3;
break;
case 'mix' :
$mode = 4;
break;
case 'nice':
$mode = 5;
break;
}
}
$e107 = e107::getInstance();
switch ($mode)
{
case 0: // folder name only.
$tmp = array(
'{e_MEDIA_FILE}' => $e107::getFolder('media_files'),
'{e_MEDIA_VIDEO}' => $e107::getFolder('media_videos'),
'{e_MEDIA_IMAGE}' => $e107::getFolder('media_images'),
'{e_MEDIA_ICON}' => $e107::getFolder('media_icons'),
'{e_AVATAR}' => $e107::getFolder('avatars'),
'{e_WEB_JS}' => $e107::getFolder('web_js'),
'{e_WEB_CSS}' => $e107::getFolder('web_css'),
'{e_WEB_IMAGE}' => $e107::getFolder('web_images'),
// '{e_WEB_PACK}' => $e107::getFolder('web_packs'),
'{e_IMAGE}' => $e107::getFolder('images'),
'{e_PLUGIN}' => $e107::getFolder('plugins'),
'{e_FILE}' => $e107::getFolder('files'),
'{e_THEME}' => $e107::getFolder('themes'),
'{e_DOWNLOAD}' => $e107::getFolder('downloads'),
'{e_ADMIN}' => $e107::getFolder('admin'),
'{e_HANDLER}' => $e107::getFolder('handlers'),
'{e_MEDIA}' => $e107::getFolder('media'),
'{e_WEB}' => $e107::getFolder('web'),
'{e_UPLOAD}' => $e107::getFolder('uploads'),
);
break;
case 1: // relative path only
$tmp = array(
'{e_MEDIA_FILE}' => e_MEDIA_FILE,
'{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO,
'{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE,
'{e_MEDIA_ICON}' => e_MEDIA_ICON,
'{e_AVATAR}' => e_AVATAR,
'{e_IMPORT}' => e_IMPORT,
'{e_WEB_JS}' => e_WEB_JS,
'{e_WEB_CSS}' => e_WEB_CSS,
'{e_WEB_IMAGE}' => e_WEB_IMAGE,
// '{e_WEB_PACK}' => e_WEB_PACK,
'{e_IMAGE}' => e_IMAGE,
'{e_PLUGIN}' => e_PLUGIN,
'{e_FILE}' => e_FILE,
'{e_THEME}' => e_THEME,
'{e_DOWNLOAD}' => e_DOWNLOAD,
'{e_ADMIN}' => e_ADMIN,
'{e_HANDLER}' => e_HANDLER,
'{e_MEDIA}' => e_MEDIA,
'{e_WEB}' => e_WEB,
'{e_UPLOAD}' => e_UPLOAD,
);
break;
case 2: // absolute path only
$tmp = array(
'{e_MEDIA_FILE}' => e_MEDIA_FILE_ABS,
'{e_MEDIA_VIDEO}' => e_MEDIA_VIDEO_ABS,
'{e_MEDIA_IMAGE}' => e_MEDIA_IMAGE_ABS,
'{e_MEDIA_ICON}' => e_MEDIA_ICON_ABS,
'{e_AVATAR}' => e_AVATAR_ABS,
'{e_WEB_JS}' => e_JS_ABS,
'{e_WEB_CSS}' => e_CSS_ABS,
'{e_WEB_IMAGE}' => e_WEB_IMAGE_ABS,
// '{e_WEB_PACK}' => e_PACK_ABS,
'{e_IMAGE}' => e_IMAGE_ABS,
'{e_PLUGIN}' => e_PLUGIN_ABS,
'{e_FILE}' => e_FILE_ABS, // deprecated
'{e_THEME}' => e_THEME_ABS,
'{e_DOWNLOAD}' => e_HTTP . 'request.php?',// FIXME - we need solution!
'{e_ADMIN}' => e_ADMIN_ABS,
//'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available
'{e_MEDIA}' => e_MEDIA_ABS,
'{e_WEB}' => e_WEB_ABS,
'{e_BASE}' => e_HTTP,
);
break;
case 3: // full path (e.g http://domain.com/e107_images/)
$tmp = array(
'{e_MEDIA_FILE}' => SITEURLBASE . e_MEDIA_FILE_ABS,
'{e_MEDIA_VIDEO}' => SITEURLBASE . e_MEDIA_VIDEO_ABS,
'{e_MEDIA_IMAGE}' => SITEURLBASE . e_MEDIA_IMAGE_ABS,
'{e_MEDIA_ICON}' => SITEURLBASE . e_MEDIA_ICON_ABS,
'{e_AVATAR}' => SITEURLBASE . e_AVATAR_ABS,
'{e_WEB_JS}' => SITEURLBASE . e_JS_ABS,
'{e_WEB_CSS}' => SITEURLBASE . e_CSS_ABS,
'{e_WEB_IMAGE}' => SITEURLBASE . e_WEB_IMAGE_ABS,
// '{e_WEB_PACK}' => SITEURLBASE.e_PACK_ABS,
'{e_IMAGE}' => SITEURLBASE . e_IMAGE_ABS,
'{e_PLUGIN}' => SITEURLBASE . e_PLUGIN_ABS,
'{e_FILE}' => SITEURLBASE . e_FILE_ABS, // deprecated
'{e_THEME}' => SITEURLBASE . e_THEME_ABS,
'{e_DOWNLOAD}' => SITEURLBASE . e_HTTP . 'request.php?',// FIXME - we need solution!
'{e_ADMIN}' => SITEURLBASE . e_ADMIN_ABS,
//'{e_HANDLER}' => e_HANDLER_ABS, - no ABS path available
'{e_MEDIA}' => SITEURLBASE . e_MEDIA_ABS,
'{e_WEB}' => SITEURLBASE . e_WEB_ABS,
'{e_BASE}' => SITEURL,
);
break;
case 4: // absolute & relative paths
$url = $this->createConstants($url, 3);
$url = $this->createConstants($url, 2);
$url = $this->createConstants($url, 1);
return $url;
break;
case 5: // nice urls - e.g. e_MEDIA_VIDEO/mystream.flv
$url = $this->createConstants($url, 4);
return str_replace($this->getUrlConstants('sc'), $this->getUrlConstants('raw'), $url);
break;
default:
$tmp = array();
break;
}
$hasCDN = strpos($url, '//') === 0;
foreach ($tmp as $key => $val)
{
// Fix - don't break the CDN '//cdn.com' URLs
if ($hasCDN && $val === '/')
{
continue;
}
$len = strlen($val);
if (substr($url, 0, $len) == $val)
{
// replace the first instance only
return substr_replace($url, $key, 0, $len);
}
}
return $url;
}
//FIXME - $match not used?
/**
* @param $text
* @param $match
* @return array|string|string[]|null
*/
public function e_highlight($text, $match)
{
$tags = array();
preg_match_all('#<[^>]+>#', $text, $tags);
$text = preg_replace('#<[^>]+>#', '<|>', $text);
$text = preg_replace('#(\b".$match."\b)#i', '<span class="searchhighlight">\\1</span>', $text);
foreach ($tags[0] as $tag)
{
$text = preg_replace('#<\|>#', $tag, $text, 1);
}
return $text;
}
/**
* Convert Text to a suitable format for use in emails. eg. relative links will be replaced with full links etc.
*
* @param string $text
* @param boolean $posted - if the text has been posted. (uses stripslashes etc)
* @param string $mods - flags for text transformation.
*/
public function toEmail($text, $posted = '', $mods = 'parse_sc, no_make_clickable')
{
if ($posted === true)
{
if (MAGIC_QUOTES_GPC)
{
$text = stripslashes($text);
}
$text = preg_replace('#\[(php)#i', '&#91;\\1', $text);
}
$text = (strtolower($mods) !== 'rawtext') ? $this->replaceConstants($text, 'full') : $text;
if ($this->isHtml($text))
{
$text = str_replace(array('[html]', '[/html]'), '', $text);
$text = html_entity_decode($text, ENT_COMPAT, 'UTF-8');
}
else
{
$text = $this->toHTML($text, true, $mods);
}
return $text;
}
/**
* Given an email address, returns a link including with obfuscated text.
* e-email css in e107.css inserts the user/domain data for display.
*
* @param string $email
* @param string $words [optional] text to display
* @param null $subject [optional] default subject for email.
* @return string
*/
public function emailObfuscate($email, $words = null, $subject = null)
{
if (strpos($email, '@') === false)
{
return '';
}
if ($subject)
{
$subject = '?subject=' . $subject;
}
list($name, $address) = explode('@', $email, 2);
if (empty($words))
{
$words = '&#64;';
$user = "data-user='" . $this->obfuscate($name) . "'";
$dom = "data-dom='" . $this->obfuscate($address) . "'";
}
else
{
$user = '';
$dom = '';
}
$url = 'mailto:' . $email . $subject;
$safe = $this->obfuscate($url);
return "<a class='e-email' {$user} {$dom} rel='external' href='" . $safe . "'>" . $words . '</a>';
}
/**
* Obfuscate text from bots using Randomized encoding.
*
* @param $text
* @return string
*/
public function obfuscate($text)
{
$ret = '';
foreach (str_split($text) as $letter)
{
switch (mt_rand(1, 3))
{
// HTML entity code
case 1:
$ret .= '&#' . ord($letter) . ';';
break;
// Hex character code
case 2:
$ret .= '&#x' . dechex(ord($letter)) . ';';
break;
// Raw (no) encoding
case 3:
$ret .= $letter;
}
}
return $ret;
}
/**
* @param $name
* @return array|e_parse_shortcode|null
*/
public function __get($name)
{
switch ($name)
{
case 'e_sc':
$ret = e107::getScParser();
break;
default:
// trigger_error('$e107->$'.$name.' not defined', E_USER_WARNING);
return null;
break;
}
$this->$name = $ret;
return $ret;
}
// Formerly located in e_parser --------------------------
/**
* Merge default 'global' attributes into assigned tags.
*/
private function compileAttributeDefaults()
{
foreach ($this->allowedAttributes as $tag => $array)
{
if ($tag === 'default')
{
continue;
}
foreach ($this->allowedAttributes['default'] as $def)
{
$this->allowedAttributes[$tag][] = $def;
}
}
}
/**
* Used by e_parse to start
*/
public function init()
{
if (defined('FONTAWESOME'))
{
$this->fontawesome = (int) FONTAWESOME;
}
if (defined('BOOTSTRAP'))
{
$this->bootstrap = (int) BOOTSTRAP;
}
$this->modRewriteMedia = deftrue('e_MOD_REWRITE_MEDIA');
if (defined('e_HTTP_STATIC'))
{
$this->staticUrl = e_HTTP_STATIC;
}
// Preprocess the supermods to be useful default arrays with all values
foreach ($this->e_SuperMods as $key => $val)
{
// precalculate super defaults
$this->e_SuperMods[$key] = array_merge($this->e_optDefault, $this->e_SuperMods[$key]);
$this->e_SuperMods[$key]['context'] = $key;
}
$this->allowedTags = array('html', 'body', 'div', 'a', 'img', 'table', 'tr', 'td', 'th', 'tbody', 'thead', 'colgroup', 'b',
'i', 'pre', 'code', 'strong', 'u', 'em', 'ul', 'ol', 'li', 'img', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p',
'section', 'article', 'blockquote', 'hgroup', 'aside', 'figure', 'figcaption', 'abbr', 'span', 'audio', 'video', 'source', 'br',
'small', 'caption', 'noscript', 'hr', 'section', 'iframe', 'sub', 'sup', 'cite', 'x-bbcode', 'label', 'dl', 'dt', 'dd',
);
$this->scriptTags = array('script', 'applet', 'form', 'input', 'button', 'embed', 'object', 'ins', 'select', 'textarea'); //allowed when $pref['post_script'] is enabled.
$this->allowedAttributes = array(
'default' => array('id', 'style', 'class', 'title', 'lang', 'accesskey'),
'img' => array('src', 'alt', 'width', 'height'),
'a' => array('href', 'target', 'rel'),
'script' => array('type', 'src', 'language', 'async'),
'iframe' => array('src', 'frameborder', 'width', 'height', 'allowfullscreen', 'allow'),
'input' => array('type', 'name', 'value'),
'form' => array('action', 'method', 'target'),
'audio' => array('src', 'controls', 'autoplay', 'loop', 'muted', 'preload'),
'video' => array('autoplay', 'controls', 'height', 'loop', 'muted', 'poster', 'preload', 'src', 'width'),
'table' => array('border', 'cellpadding', 'cellspacing'), // BC Fix.
'td' => array('colspan', 'rowspan', 'name', 'bgcolor'),
'th' => array('colspan', 'rowspan'),
'col' => array('span'),
'embed' => array('src', 'wmode', 'type', 'width', 'height'),
'x-bbcode' => array('alt'),
'label' => array('for'),
'source' => array('media', 'sizes', 'src', 'srcset', 'type'),
);
$this->scriptAttributes = array('onclick', 'onchange', 'onblur', 'onload', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup',
'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
'onwheel', 'oncopy', 'oncut', 'onpaste'
);
$this->blockTags = array('p', 'pre', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote'); // element includes its own line-break.
$this->badAttrValues = array('javascript[\s]*?:', 'alert\(', 'vbscript[\s]*?:', 'data:text\/html', 'mhtml[\s]*?:', 'data:[\s]*?image');
$this->replaceAttrValues = array(
'default' => array()
);
}
/**
* Add Allowed Tags.
*
* @param string
*/
public function addAllowedTag($tag)
{
$this->allowedTags[] = $tag;
}
/**
* @param $tag - html tag.
* @param $attArray - array of attributes. eg. array('style', 'id', 'class') etc.
*/
public function addAllowedAttribute($tag, $attArray)
{
$this->allowedAttributes[$tag] = (array) $attArray;
}
/**
* Set Allowed Tags.
*
* @param $array
*/
public function setAllowedTags($array = array())
{
$this->allowedTags = $array;
}
/**
* Set Script Access
*
* @param $val int e_UC_MEMBER, e_UC_NOBODY, e_UC_MAINADMIN or userclass number.
*/
public function setScriptAccess($val)
{
$this->scriptAccess = $val;
}
/**
* @param array $arr
* @return void
*/
public function setScriptAttibutes($arr)
{
$this->scriptAttributes = (array) $arr;
}
/**
* @return array
*/
public function getAllowedTags()
{
return $this->allowedTags;
}
/**
* @return array
*/
public function getAllowedAttributes()
{
return $this->allowedAttributes;
}
/**
* @return bool
*/
public function getScriptAccess()
{
return $this->scriptAccess;
}
/**
* @return array
*/
public function getRemoved()
{
return $this->removedList;
}
/**
* Set Allowed Attributes.
*
* @param $array
*/
public function setAllowedAttributes($array = array())
{
$this->allowedAttributes = $array;
}
/**
* Set Script Tags.
*
* @param $array
*/
public function setScriptTags($array = array())
{
$this->scriptTags = $array;
}
/**
* @param int $version
*/
public function setFontAwesome($version)
{
$this->fontawesome = (int) $version;
}
/**
* @param int $version
*/
public function setBootstrap($version)
{
$this->bootstrap = (int) $version;
}
public function setmodRewriteMedia($bool)
{
$this->modRewriteMedia = (bool) $bool;
}
/**
* Add leading zeros to a number. eg. 3 might become 000003
*
* @param $num integer
* @param $numDigits - total number of digits
* @return string number with leading zeros.
*/
public function leadingZeros($num, $numDigits)
{
return (string) sprintf('%0' . $numDigits . 'd', $num);
}
/**
* Generic variable translator for LAN definitions.
*
* @param $lan - string LAN
* @param string | array $vals - either a single value, which will replace '[x]' or an array with key=>value pairs.
* @return string
* @example $tp->lanVars("My name is [x] and I own a [y]", array("John","Cat"));
* @example $tp->lanVars("My name is [x] and I own a [y]", array('x'=>"John", 'y'=>"Cat"));
*/
public function lanVars($lan, $vals, $bold = false)
{
$array = (!is_array($vals)) ? array('x' => $vals) : $vals;
$search = array();
$replace = array();
$defaults = array('x', 'y', 'z');
foreach ($array as $k => $v)
{
if (is_numeric($k)) // convert array of numeric to x,y,z
{
$k = $defaults[$k];
}
$search[] = '[' . $k . ']';
$replace[] = ($bold === true) ? '<strong>' . $v . '</strong>' : $v;
}
return str_replace($search, $replace, $lan);
}
/**
* Return an Array of all specific tags found in an HTML document and their attributes.
*
* @param $html - raw html code
* @param $taglist - comma separated list of tags to search or '*' for all.
* @param $header - if the $html includes the html head or body tags - it should be set to true.
*/
public function getTags($html, $taglist = '*', $header = false)
{
if ($header == false)
{
$html = '<html><body>' . $html . '</body></html>';
}
$doc = $this->domObj;
$doc->preserveWhiteSpace = true;
libxml_use_internal_errors(true);
$doc->loadHTML($html);
$tg = explode(',', $taglist);
$ret = array();
foreach ($tg as $find)
{
$tmp = $doc->getElementsByTagName($find);
/**
* @var $k
* @var DOMDocument $node
*/
foreach ($tmp as $k => $node)
{
$tag = $node->nodeName;
$inner = $node->C14N();
$inner = str_replace('&#xD;', '', $inner);
foreach ($node->attributes as $attr)
{
$name = $attr->nodeName;
$value = $attr->nodeValue;
$ret[$tag][$k][$name] = $value;
}
$ret[$tag][$k]['@value'] = $inner;
}
}
if ($header == false)
{
unset($ret['html'], $ret['body']);
}
return $ret;
}
/**
* Glyph Embed Method Direct from svg file.
*
* @param string $cat far|fab|fas
* @param string $id eg. fa-search
* @param array $parm eg. ['fw'=>true]
* @return string|false
*/
private function toGlyphEmbed($cat, $id, $parm = array())
{
$dirs = ['far' => 'regular', 'fab' => 'brands', 'fas' => 'solid'];
$path = e_WEB . 'lib/font-awesome/5/svgs/';
$path .= $dirs[$cat] . '/';
$path .= str_replace('fa-', '', $id) . ".svg";
if ($ret = file_get_contents($path))
{
$class = 'svg-inline--fa ';
$class .= $id;
$class .= ' fa-w-16';
$class .= !empty($parm['fw']) ? ' fa-fw' : '';
return str_replace('<svg', '<svg class="' . $class . '" role="img" aria-hidden="true" ', $ret);
}
return false;
}
/**
* Parse xxxxx.glyph file to bootstrap glyph format.
*
* @param string $text ie. fa-xxxx, fab-xxx, fas-xxxx
* @param array|string $options = [
* 'size' => (string) 2x, 3x, 4x, or 5x
* 'fw' => (bool) Fixed-Width
* 'spin' => (bool) Spin
* 'rotate'=> (int) Rotate in Degrees.
* ]
* @example $tp->toGlyph('fab-mailchimp');
* @example $tp->toGlyph('fas-camera');
* @example $tp->toGlyph('fa-spinner', 'spin=1');
* @example $tp->toGlyph('fa-spinner', array('spin'=>1));
* @example $tp->toGlyph('fa-shield', array('rotate'=>90, 'size'=>'2x'));
*/
public function toGlyph($text, $options = ' ')
{
if (empty($text))
{
return false;
}
if (is_array($options))
{
$parm = $options;
$options = varset($parm['space'], '');
}
elseif (is_string($options) && strpos($options, '='))
{
parse_str($options, $parm);
$options = varset($parm['space'], '');
}
else
{
$parm = array();
}
$cat = '';
$name = '';
list($id) = explode('.glyph', $text, 2); // trim .glyph from the end.
$origID = $id;
if (strpos($id, '-') !== false)
{
list($cat, $name) = explode('-', $id, 2);
if (empty($name)) // eg. missing something after 'fa-'
{
return null;
}
}
$spin = null;
$rotate = null;
$fixedW = null;
$prefix = 'glyphicon glyphicon-'; // fallback
$size = null;
$tag = 'i';
// FontAwesome General settings.
switch ($cat)
{
// Core eg. e-database-32
case 'e':
$size = (substr($text, -3) === '-32') ? 'S32' : 'S16';
if (substr($text, -3) === '-24')
{
$size = 'S24';
}
return "<i class='" . $size . ' ' . $text . "'></i>";
break;
case "far":
case "fab":
case "fas":
$prefix = $cat . ' ';
$id = str_replace($cat . '-', 'fa-', $id);
/** @experimental - subject to removal at any time. */
if (!empty($parm['embed']))
{
if ($ret = $this->toGlyphEmbed($cat, $id, $parm))
{
return $ret;
}
}
break;
case "fa":
default:
if ($this->fontawesome === 5 || $this->fontawesome === 6)
{
$vr = 'fa'.$this->fontawesome.'-';
$fab = e107::getMedia()->getGlyphs($vr.'fab');
$fas = e107::getMedia()->getGlyphs($vr.'fas');
$far = e107::getMedia()->getGlyphs($vr.'far');
$shims = e107::getMedia()->getGlyphs($vr.'shims');
$fa4 = e107::getMedia()->getGlyphs('fa4');
list($tmp) = explode('-', $id);
$code = str_replace($tmp . '-', '', $id);
if (isset($shims[$code]))
{
$prefix = '';
$id = $shims[$code];
}
elseif (isset($fab[$code]))
{
$prefix = 'fab ';
}
elseif (isset($fas[$code]))
{
$prefix = 'fas '; // 'fa-solid' for fa6?
$id = 'fa-' . $code;
}
elseif (in_array($code, $far))
{
$prefix = 'far ';
}
elseif (in_array($code, $fa4))
{
$prefix = 'fa ';
$id = 'fa-' . $code;
}
else
{
$prefix = ($this->bootstrap === 3) ? 'glyphicon glyphicon-' : 'fa fa-';
}
/** @experimental - subject to removal at any time. */
if (!empty($parm['embed']))
{
$cat = trim($prefix);
if ($ret = $this->toGlyphEmbed($cat, $id, $parm))
{
return $ret;
}
}
$cat = trim($prefix);
}
elseif ($this->fontawesome === 4)
{
$fa4 = e107::getMedia()->getGlyphs('fa4');
if (isset($fa4[$name]))
{
$prefix = 'fa ';
$id = 'fa-' . $name;
}
}
elseif (strpos($text, 'glyphicon-') === 0) // Bootstrap 3
{
$prefix = 'glyphicon ';
$tag = 'span';
}
elseif (strpos($text, 'icon-') === 0) // Bootstrap 2
{
if ($this->bootstrap !== 2) // bootrap 2 icon but running bootstrap3.
{
$prefix = 'glyphicon ';
$tag = 'span';
$id = str_replace('icon-', 'glyphicon-', $id);
}
else
{
$prefix = '';
$tag = 'i';
}
}
}
if ($custom = e107::getThemeGlyphs()) // Custom Glyphs
{
foreach ($custom as $glyphConfig)
{
if (strpos($text, $glyphConfig['prefix']) === 0)
{
$prefix = $glyphConfig['class'] . ' ';
$tag = $glyphConfig['tag'];
$id = $origID;
continue;
}
}
}
else // FontAwesome shouldn't hurt legacy icons.
{
$size = !empty($parm['size']) ? ' fa-' . $parm['size'] : '';
$spin = !empty($parm['spin']) ? ' fa-spin' : '';
$rotate = !empty($parm['rotate']) ? ' fa-rotate-' . (int) $parm['rotate'] : '';
$fixedW = !empty($parm['fw']) ? ' fa-fw' : '';
}
$idAtt = (!empty($parm['id'])) ? "id='" . $parm['id'] . "' " : '';
$style = (!empty($parm['style'])) ? "style='" . $parm['style'] . "' " : '';
$class = (!empty($parm['class'])) ? $parm['class'] . ' ' : '';
$placeholder = isset($parm['placeholder']) ? $parm['placeholder'] : '';
$title = (!empty($parm['title'])) ? " title='" . $this->toAttribute($parm['title']) . "' " : '';
$text = '<' . $tag . " {$idAtt}class='" . $class . $prefix . $id . $size . $spin . $rotate . $fixedW . "' " . $style . $title . '>' . $placeholder . '</' . $tag . '>';
$text .= ($options !== false) ? $options : '';
return $text;
}
/**
* Return a Bootstrap Badge tag
*
* @param $text
* @param null $parm
* @return string
*/
public function toBadge($text, $parm = null)
{
$class = !empty($parm['class']) ? ' ' . $parm['class'] : ' bg-secondary rounded-pill badge-secondary';
return "<span class='badge" . $class . "'>" . $text . '</span>';
}
/**
* Return a Bootstrap Label tag
*
* @param $text
* @param null $type
* @return string
*/
public function toLabel($text, $type = null)
{
if ($type === null)
{
$type = 'default';
}
$tmp = explode(',', $text);
$opt = array();
foreach ($tmp as $v)
{
$opt[] = "<span class='label label-" . $type . "'>" . $v . '</span>';
}
return implode(' ', $opt);
}
/**
* Take a file-path and convert it to a download link.
*
* @param $text
* @param array $parm
* @return string
*/
public function toFile($text, $parm = array())
{
$srch = array(
'{e_MEDIA_FILE}' => 'e_MEDIA_FILE/',
'{e_PLUGIN}' => 'e_PLUGIN/'
);
$link = e_HTTP . 'request.php?file=' . str_replace(array_keys($srch), $srch, $text);
if (!empty($parm['raw']))
{
return $link;
}
return "<a href='" . $link . "'>-attachment-</a>"; //TODO Add pref for this.
}
/**
* Render an avatar based on supplied user data or current user when missing.
*
* @param array $userData - user data from e107_user. ie. user_image, user_id etc.
* @param array $options = [
* 'w' => (int) image width in px
* 'h' => (int) image height in px
* 'crop' => *int|bool) enables cropping when true
* 'shape' => (string) (optional) rounded|circle|thumbnail
* 'id' => (string) 'id' attribute will be added to tag.
* 'class' => (string) override default 'class' attribute in tag.
* 'alt' => (string) override default 'alt' attribute in tag.
* 'base64' => (bool) use embedded base64 for image src.
* 'hd' => (bool) double the resolution of the image. Useful for retina displays.
* 'type' => (string) when set to 'url' returns the URL value instead of the tag.
* 'style' => (string) sets the style attribute.
* 'mode' => (string) 'full' url mode.
* ]
* @return string <img> tag of avatar.
*/
public function toAvatar($userData = null, $options = array())
{
$tp = e107::getParser();
$width = !empty($options['w']) ? intval($options['w']) : $tp->thumbWidth;
$height = ($tp->thumbHeight !== 0) ? $tp->thumbHeight : '';
$crop = isset($options['crop']) ? $options['crop'] : $tp->thumbCrop;
$linkStart = '';
$linkEnd = '';
$full = !empty($options['base64']) ? true : false;
$file = '';
if (!empty($options['mode']) && $options['mode'] === 'full')
{
$full = true;
}
if (!empty($options['h']))
{
$height = intval($options['h']);
}
if (!empty($options['hd'])) // Fix resolution on Retina display.
{
$width *= 2;
$height *= 2;
}
if ($userData === null && USERID)
{
$userData = array();
$userData['user_id'] = USERID;
$userData['user_image'] = deftrue('USERIMAGE');
$userData['user_name'] = deftrue('USERNAME');
$userData['user_currentvisit'] = deftrue('USERCURRENTVISIT');
}
$image = (!empty($userData['user_image'])) ? varset($userData['user_image']) : null;
$genericFile = e_IMAGE . 'generic/blank_avatar.jpg';
$genericImg = $tp->thumbUrl($genericFile, 'w=' . $width . '&h=' . $height, true, $full);
if (!empty($image))
{
if (strpos($image, '://') !== false) // Remote Image
{
$url = $image;
}
elseif (strpos($image, '-upload-') === 0)
{
$image = substr($image, 8); // strip the -upload- from the beginning.
if (file_exists(e_AVATAR_UPLOAD . $image))
{
$file = e_AVATAR_UPLOAD . $image;
$url = $tp->thumbUrl($file, 'w=' . $width . '&h=' . $height . '&crop=' . $crop, false, $full);
}
else
{
$file = $genericFile;
$url = $genericImg;
}
}
elseif (file_exists(e_AVATAR_DEFAULT . $image)) // User-Uplaoded Image
{
$file = e_AVATAR_DEFAULT . $image;
$url = $tp->thumbUrl($file, 'w=' . $width . '&h=' . $height . '&crop=' . $crop, false, $full);
}
else // Image Missing.
{
$url = $genericImg;
$file = $genericFile;
}
}
else // No image provided - so send generic.
{
$url = $genericImg;
$file = $genericFile;
}
if (!empty($options['base64'])) // embed image data into URL.
{
$content = e107::getFile()->getRemoteContent($url);
if (!empty($content))
{
if(!empty($file))
{
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
}
else
{
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
}
$url = 'data:image/' . $ext . ';base64,' . base64_encode($content);
}
}
if (!empty($options['hd'])) // Fix resolution on Retina display.
{
$width = $width / 2;
$height = ($height / 2);
}
if (($url == $genericImg) && !empty($userData['user_id']) && (($userData['user_id'] == USERID)) && !empty($options['link']))
{
$linkStart = "<a class='e-tip' title=\"" . LAN_EDIT . "\" href='" . e107::getUrl()->create('user/myprofile/edit') . "'>";
$linkEnd = '</a>';
}
$title = (ADMIN) ? $image : $tp->toAttribute($userData['user_name']);
$shape = (!empty($options['shape'])) ? 'img-' . $options['shape'] : 'img-rounded rounded';
if ($shape === 'img-circle')
{
$shape .= ' rounded-circle';
}
if (!empty($options['type']) && $options['type'] === 'url')
{
return $url;
}
if (!empty($options['alt']))
{
$title = $tp->toAttribute($options['alt']);
}
$heightInsert = empty($height) ? '' : "height='" . $height . "'";
$id = (!empty($options['id'])) ? "id='" . $options['id'] . "' " : '';
$classOnline = (!empty($userData['user_currentvisit']) && intval($userData['user_currentvisit']) > (time() - 300)) ? ' user-avatar-online' : '';
$class = !empty($options['class']) ? $options['class'] : $shape . ' user-avatar';
$style = !empty($options['style']) ? " style='" . $options['style'] . "'" : '';
$loading = !empty($options['loading']) ? " loading='" . $options['loading'] . "'" : " loading='lazy'"; // default to lazy.
$text = $linkStart;
$text .= '<img ' . $id . "class='" . $class . $classOnline . "' alt=\"" . $title . "\" src='" . $url . "' width='" . $width . "' " . $heightInsert . $style . $loading . ' />';
$text .= $linkEnd;
// return $url;
return $text;
}
/**
* Display an icon.
*
* @param string $icon
* @example $tp->toIcon("{e_IMAGES}icons/something.png");
*/
public function toIcon($icon = '', $parm = array())
{
if (empty($icon))
{
return null;
}
// if(strpos($icon,'e_MEDIA_IMAGE')!==false)
// {
// return "<div class='alert alert-danger'>Use \$tp->toImage() instead of toIcon() for ".$icon."</div>"; // debug info only.
// }
if (strpos($icon, '<i ') === 0) // if it's html (ie. css sprite) return the code.
{
return $icon;
}
$ext = pathinfo($icon, PATHINFO_EXTENSION);
$dimensions = null;
if (!$ext || $ext === 'glyph') // Bootstrap or Font-Awesome.
{
return $this->toGlyph($icon, $parm);
}
if (strpos($icon, 'e_MEDIA_IMAGE') !== false)
{
$path = $this->thumbUrl($icon);
$dimensions = $this->thumbDimensions();
}
elseif ($icon[0] === '{')
{
$path = $this->replaceConstants($icon, 'abs');
}
elseif (!empty($parm['legacy']))
{
$legacyList = (!is_array($parm['legacy'])) ? array($parm['legacy']) : $parm['legacy'];
foreach ($legacyList as $legPath)
{
$legacyPath = $legPath . $icon;
$filePath = $this->replaceConstants($legacyPath);
if (is_readable($filePath))
{
$path = $this->replaceConstants($legacyPath, 'full');
break;
}
}
if (empty($path))
{
$log = e107::getLog();
$log->addDebug('Broken Icon Path: ' . $icon . "\n" . print_r(debug_backtrace(null, 2), true), false)->save('IMALAN_00');
e107::getDebug()->log('Broken Icon Path: ' . $icon);
return null;
}
}
else
{
$path = $icon;
}
$alt = (!empty($parm['alt'])) ? $this->toAttribute($parm['alt']) : basename($path);
$class = (!empty($parm['class'])) ? $parm['class'] : 'icon';
if ($ext === 'svg')
{
$class .= ' icon-svg fa-2x';
if (!empty($parm['size']))
{
$class .= ' icon-svg-' . $parm['size'];
}
}
return "<img class='" . $class . "' src='" . $path . "' alt='" . $alt . "' " . $dimensions . ' />';
}
/**
* Render an img tag.
*
* @param string $file
* @param array $parm = [
* 'w' => (int) Width in px
* 'h' => (int) Height in px
* 'alt' => (string) Alt text.
* 'class' => (string)
* 'id' => (string)
* 'loading' => (string)
* 'legacy' => (array) Usually a legacy path like {e_FILE}
* 'type' => (array) Force the returned image to be a jpg, webp etc.
* ]
* @return string
* @example $tp->toImage('welcome.png', array('legacy'=>{e_IMAGE}newspost_images/','w'=>200));
*/
public function toImage($file, $parm = array())
{
if (strpos($file, 'e_AVATAR') !== false)
{
return "<div class='alert alert-danger'>Use \$tp->toAvatar() instead of toImage() for " . $file . '</div>'; // debug info only.
}
if (empty($file) && empty($parm['placeholder']))
{
return null;
}
if (!empty($file) && (strpos($file, 'http') === false))
{
$srcset = null;
$path = null;
$file = trim($file);
$ext = pathinfo($file, PATHINFO_EXTENSION);
$accepted = array('jpg', 'gif', 'png', 'jpeg', 'svg', 'webp');
if (!in_array($ext, $accepted))
{
return null;
}
}
$tp = $this;
if(($alt = $this->getImageAltCacheFile($file)) && empty($parm['alt']))
{
$parm['alt'] = $alt;
}
// e107::getDebug()->log($file);
// e107::getDebug()->log($parm);
if (strpos($file, 'http') === 0)
{
$path = $file;
}
elseif (strpos($file, 'e_MEDIA') !== false || strpos($file, 'e_THEME') !== false || strpos($file, 'e_PLUGIN') !== false || strpos($file, '{e_IMAGE}') !== false) //v2.x path.
{
if (!isset($parm['w']) && !isset($parm['h']))
{
$parm = (array) $parm;
$parm['w'] = $tp->thumbWidth();
$parm['h'] = $tp->thumbHeight();
$parm['crop'] = $tp->thumbCrop();
$parm['x'] = $tp->thumbEncode();
}
unset($parm['src']);
$path = $tp->thumbUrl($file, $parm);
if (empty($parm['w']) && empty($parm['h']))
{
$parm['srcset'] = false;
}
elseif (!isset($parm['srcset']))
{
$srcSetParm = $parm;
if (!isset($parm['size']))
{
$srcSetParm['size'] = (varset($parm['w']) < 100) ? '4x' : '2x';
}
else
{
unset($parm['size']);
}
$parm['srcset'] = $tp->thumbSrcSet($file, $srcSetParm);
}
}
elseif (!empty($file) && $file[0] === '{') // Legacy v1.x path. Example: {e_PLUGIN}myplugin/images/fixedimage.png
{
$path = $tp->replaceConstants($file, 'abs');
}
elseif(!empty($parm['legacy']) && !empty($file)) // Search legacy path for image in a specific folder. No path, only file name provided.
{
$legacyPath = rtrim($parm['legacy'], '/') . '/' . $file;
$filePath = $tp->replaceConstants($legacyPath);
if (is_readable($filePath))
{
$path = $tp->replaceConstants($legacyPath, 'abs');
}
else
{
$log = e107::getLog();
$log->addDebug('Broken Image Path: ' . $legacyPath . "\n" . print_r(debug_backtrace(0, 2), true), false)->save('IMALAN_00');
e107::getDebug()->log('Broken Image Path: ' . $legacyPath);
}
}
else // usually http://....
{
$path = $file;
}
if (empty($path) && !empty($parm['placeholder']))
{
$path = $tp->thumbUrl($file, $parm);
}
$id = (!empty($parm['id'])) ? 'id="' . $parm['id'] . '" ' : '';
$class = (!empty($parm['class'])) ? $parm['class'] : 'img-responsive img-fluid';
$alt = (!empty($parm['alt'])) ? $tp->toAttribute($parm['alt']) : basename($file);
$style = (!empty($parm['style'])) ? 'style="' . $parm['style'] . '" ' : '';
$srcset = (!empty($parm['srcset'])) ? 'srcset="' . $parm['srcset'] . '" ' : '';
$width = (!empty($parm['w'])) ? 'width="' . (int) $parm['w'] . '" ' : '';
$title = (!empty($parm['title'])) ? 'title="' . $parm['title'] . '" ' : '';
$height = !empty($parm['h']) ? 'height="' . (int) $parm['h'] . '" ' : '';
$loading = !empty($parm['loading']) ? 'loading="' . $parm['loading'] . '" ' : ''; // eg. lazy, eager, auto
if (isset($parm['width'])) // width attribute override (while retaining w)
{
$width = 'width="' . $parm['width'] . '" ';
}
if (isset($parm['height'])) // height attribute override (while retaining h)
{
$height = 'height="' . $parm['height'] . '" ';
}
$html = '';
/*
if($this->convertToWebP)
{
$source = $tp->thumbUrl($file, $parm);
$html = "<picture class=\"{$class}\">\n";
if(!empty($parm['srcset']))
{
list($webPSourceSet, $webPSize) = explode(' ', $parm['srcset']);
$html .= '<source type="image/webp" srcset="' . $webPSourceSet . ' ' . $webPSize . '">';
$html .= "\n";
$html .= '<source type="image/' . str_replace('jpg', 'jpeg', $ext) . '" srcset="' . $parm['srcset'] . '">';
$html .= "\n";
$srcset = ''; // remove it from the img tag below.
}
$html .= '<source type="image/webp" srcset="' . $source . '">';
$html .= "\n";
}*/
if (empty($path))
{
return null;
}
$html .= "<img {$id}class=\"{$class}\" src=\"" . $path . '" alt="' . $alt . '" ' . $srcset . $width . $height . $style . $loading . $title . ' />';
// $html .= ($this->convertToWebP) ? "\n</picture>" : '';
return $html;
}
/**
* Check if a string contains bbcode.
*
* @param $text
* @return bool
*/
public function isBBcode($text)
{
if (!is_string($text))
{
return false;
}
if (strpos($text, '[') === false || preg_match('#(?<=<)\w+(?=[^<]*?>)#', $text))
{
return false;
}
$bbsearch = array('[/img]', '[/h]', '[/b]', '[/link]', '[/right]', '[/center]', '[/flash]', '[/code]', '[/table]');
foreach ($bbsearch as $v)
{
if (strpos($text, $v) !== false)
{
return true;
}
}
return false;
}
/**
* Check if a string is HTML
*
* @param $text
* @return bool
*/
public function isHtml($text)
{
if (!is_string($text))
{
return false;
}
if (strpos($text, '[html]') !== false)
{
return true;
}
if ($this->isBBcode($text))
{
return false;
}
if (preg_match('#(?<=<)\w+(?=[^<]*?>)#', $text))
{
return true;
}
return false;
}
/**
* Check if string is json and parse or return false.
*
* @param $text
* @return bool|mixed return false if not json, and json values if true.
*/
public function isJSON($text)
{
if (!is_string($text))
{
return false;
}
if (strpos($text, '{') === 0 || strpos($text, '[') === 0) // json
{
$dat = json_decode($text, true);
if (json_last_error() != JSON_ERROR_NONE)
{
// e107::getDebug()->log("Json data found");
return false;
}
return $dat;
}
return false;
}
/**
* Checks if string is valid UTF-8.
*
* Try to detect UTF-8 using mb_detect_encoding(). If mb string extension is
* not installed, we try to use a simple UTF-8-ness checker using a regular
* expression originally created by the W3C. But W3C's function scans the
* entire strings and checks that it conforms to UTF-8.
*
* @see http://w3.org/International/questions/qa-forms-utf-8.html
*
* So this function is faster and less specific. It only looks for non-ascii
* multibyte sequences in the UTF-8 range and also to stop once it finds at
* least one multibytes string. This is quite a lot faster.
*
* @param $string string string being checked.
* @return bool Returns true if $string is valid UTF-8 and false otherwise.
*/
public function isUTF8($string)
{
if (function_exists('mb_check_encoding'))
{
return (mb_check_encoding($string, 'UTF-8'));
}
return (bool) preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)+%xs', $string);
}
/**
* Check if a file is an video or not.
*
* @param $file string
* @return boolean
*/
public function isVideo($file)
{
if (!is_string($file))
{
return false;
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
return $ext === 'youtube' || $ext === 'youtubepl' || $ext === 'mp4';
}
/**
* Check if a file is an image or not.
*
* @param $file string
* @return boolean
*/
public function isImage($file)
{
if (!is_string($file))
{
return false;
}
if (strpos($file, '{e_') === 0)
{
$file = $this->replaceConstants($file);
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
return ($ext === 'jpg' || $ext === 'png' || $ext === 'gif' || $ext === 'jpeg' || $ext === 'webp');
}
/**
* @param $file
* @param array $parm
* @return string
*/
public function toAudio($file, $parm = array())
{
$file = $this->replaceConstants($file, 'abs');
$ext = pathinfo($file, PATHINFO_EXTENSION);
switch (strtolower($ext))
{
case 'wav':
$mime = 'audio/wav';
break;
case 'ogg':
$mime = 'audio/ogg';
break;
case 'mp3':
default:
$mime = 'audio/mpeg';
break;
}
if(!empty($parm['mime']))
{
$mime = $parm['mime'];
}
$autoplay = !empty($parm['autoplay']) ? 'autoplay ' : '';
$controls = !empty($parm['controls']) ? 'controls' : '';
$text = '<audio controls style="max-width:100%" ' . $autoplay . $controls . '>';
$text .= "\n";
$text .= '<source src="' . $file . '" type="' . $mime . '">';
$text .= "\n";
$text .= 'Your browser does not support the audio tag.';
$text .= "\n";
$text .= '</audio>';
return $text;
}
/**
* Display a Video file.
*
* @param string $file - format: id.type eg. x123dkax.youtube
* @param array $parm - set to 'tag' to return an image thumbnail and 'src' to return the src url or 'video' for a small video thumbnail.
*/
public function toVideo($file, $parm = array())
{
if (empty($file))
{
return false;
}
$type = pathinfo($file, PATHINFO_EXTENSION);
$id = str_replace('.' . $type, '', $file);
$thumb = vartrue($parm['thumb']);
$mode = varset($parm['mode'], false); // tag, url
$pref = e107::getPref();
$ytpref = array();
foreach ($pref as $k => $v) // Find all Youtube Prefs.
{
if (strpos($k, 'youtube_') === 0)
{
$key = substr($k, 8);
$ytpref[$key] = $v;
}
}
unset($ytpref['bbcode_responsive']); // do not include in embed code.
if (!empty($ytpref['cc_load_policy']))
{
$ytpref['cc_lang_pref'] = e_LAN; // switch captions with chosen user language.
}
$ytqry = http_build_query($ytpref, '', '&amp;');
$defClass = !empty($this->bootstrap) ? 'embed-responsive embed-responsive-16by9 ratio ratio-16x9' : 'video-responsive'; // levacy backup.
if ($type === 'youtube')
{
// $thumbSrc = "https://i1.ytimg.com/vi/".$id."/0.jpg";
$thumbSrc = 'https://i1.ytimg.com/vi/' . $id . '/mqdefault.jpg';
$video = '<iframe class="embed-responsive-item" width="560" height="315" src="//www.youtube.com/embed/' . $id . '?' . $ytqry . '" style="background-size: 100%;background-image: url(' . $thumbSrc . ');border:0px" allowfullscreen></iframe>';
$url = 'http://youtu.be/' . $id;
if ($mode === 'url')
{
return $url;
}
if ($thumb === 'tag')
{
return "<img class='img-responsive img-fluid' src='" . $thumbSrc . "' alt='Youtube Video' style='width:" . vartrue($parm['w'], '80') . "px'/>";
}
if ($thumb === 'email')
{
$thumbSrc = 'http://i1.ytimg.com/vi/' . $id . '/maxresdefault.jpg'; // 640 x 480
$filename = 'temp/yt-thumb-' . md5($id) . '.jpg';
$filepath = e_MEDIA . $filename;
if (!file_exists($filepath))
{
e107::getFile()->getRemoteFile($thumbSrc, $filename, 'media');
}
return "<a href='" . $url . "'><img class='video-responsive video-thumbnail' src='{e_MEDIA}" . $filename . "' alt='" . LAN_YOUTUBE_VIDEO . "' title='" . LAN_CLICK_TO_VIEW . "' />
<div class='video-thumbnail-caption'><small>" . LAN_CLICK_TO_VIEW . '</small></div></a>';
}
if ($thumb === 'src')
{
return $thumbSrc;
}
if ($thumb === 'video')
{
return '<div class="' . $defClass . ' video-thumbnail thumbnail">' . $video . '</div>';
}
return '<div class="' . $defClass . ' ' . vartrue($parm['class']) . '">' . $video . '</div>';
}
if ($type === 'youtubepl')
{
if ($thumb === 'tag')
{
$thumbSrc = e107::getMedia()->getThumb($id);
if (empty($thumbSrc))
{
$thumbSrc = e_IMAGE_ABS . 'generic/playlist_120.png';
}
return "<img class='img-responsive img-fluid' src='" . $thumbSrc . "' alt='" . LAN_YOUTUBE_PLAYLIST . "' style='width:" . vartrue($parm['w'], '80') . "px'/>";
}
if ($thumb === 'src')
{
$thumb = e107::getMedia()->getThumb($id);
if (!empty($thumb))
{
return $thumb;
}
// return "https://cdn0.iconfinder.com/data/icons/internet-2-2/64/youtube_playlist_videos_vid_web_online_internet-256.png";
return e_IMAGE_ABS . 'generic/playlist_120.png';
}
$video = '<iframe width="560" height="315" src="https://www.youtube.com/embed/videoseries?list=' . $id . '" style="border:0" allowfullscreen></iframe>';
return '<div class="' . $defClass . ' ' . vartrue($parm['class']) . '">' . $video . '</div>';
}
if ($type === 'mp4')
{
$file = $this->replaceConstants($file, 'abs');
if ($mode === 'url')
{
return $file;
}
$width = varset($parm['w'], 320);
$height = varset($parm['h'], 240);
$mime = varset($parm['mime'], 'video/mp4');
if($height === 0)
{
$height = 'auto';
}
return '
<div class="video-responsive">
<video width="' . $width . '" height="' . $height . '" controls>
<source src="' . $file . '" type="' . $mime . '">
Your browser does not support the video tag.
</video>
</div>';
}
return false;
}
/**
* Display a Date in the browser.
* Includes support for 'livestamp' (http://mattbradley.github.io/livestampjs/)
*
* @param integer $datestamp - unix timestamp
* @param string $format - short | long | relative
* @return string converted date (html)
*/
public function toDate($datestamp = null, $format = 'short')
{
if (!is_numeric($datestamp))
{
return null;
}
$value = e107::getDate()->convert_date($datestamp, $format);
$inc = ($format === 'relative') ? ' data-livestamp="' . $datestamp . '"' : '';
return '<span' . $inc . '>' . $value . '</span>';
}
/**
* Parse new <x-bbcode> tags into bbcode output.
*
* @param bool $retainTags : when you want to replace html and retain the <bbcode> tags wrapping it.
* @return string html
*/
public function parseBBTags($text, $retainTags = false)
{
$stext = str_replace('&quot;', '"', $text);
$bbcodes = $this->getTags($stext, 'x-bbcode');
foreach ($bbcodes as $v)
{
foreach ($v as $val)
{
$tag = base64_decode($val['alt']);
$repl = ($retainTags == true) ? '$1' . $tag . '$2' : $tag;
// $text = preg_replace('/(<x-bbcode[^>]*>).*(<\/x-bbcode>)/i',$repl, $text);
$text = preg_replace('/(<x-bbcode alt=(?:&quot;|")' . $val['alt'] . '(?:&quot;|")>).*(<\/x-bbcode>)/i', $repl, $text);
}
}
return $text;
}
/**
* Filters/Validates using the PHP5 filter_var() method.
*
* @param string|array $text
* @param string $type str|int|email|url|w|wds|file
*
* If the type is "str" (default), HTML tags are stripped, and quotation marks are escaped for
* HTML with the intention of making the string safe to use in both concatenated SQL queries and
* HTML code.
*
* Despite the intention, strings returned by this function should still be specified as values
* in SQL prepared statements or surrounded by {@see mysqli_real_escape_string()} if the string
* is to be written to the database.
* @return string|boolean| array
*/
public function filter($text, $type = 'str', $validate = false)
{
if (empty($text))
{
return $text;
}
$regex = array(
'w' => '/[^\w]/',
'd' => '/[^\d]/',
'wd' => '/[^\w]/',
'wds' => '/[^\w ]/',
'file' => '/[^\w_\.-]/',
'version' => '/[^\d_\.]/',
);
$ret = '';
switch ($type)
{
case 'w':
case 'd':
case 'wd':
case 'wds':
case 'version':
if ($validate === true)
{
trigger_error("Unsupported type '" . $type . "' for validation used in e107::getParser()->filter().", E_USER_WARNING);
}
else
{
$reg = $regex[$type];
$ret = preg_replace($reg, '', $text);
}
break;
case 'file':
if ($validate === true)
{
trigger_error("Unsupported type '" . $type . "' used in e107::getParser()->filter().", E_USER_WARNING);
}
else
{
$reg = $regex['file'];
$ret = preg_replace('/[^\w_\.-]/', '-', $text);
}
break;
default:
if ($validate === false)
{
$filterTypes = array(
'int' => FILTER_SANITIZE_NUMBER_INT,
'str' => function($input)
{
return htmlspecialchars(strip_tags($input), ENT_QUOTES);
},
'email' => FILTER_SANITIZE_EMAIL,
'url' => FILTER_SANITIZE_URL,
'enc' => FILTER_SANITIZE_ENCODED
);
}
else
{
$filterTypes = array(
'int' => FILTER_VALIDATE_INT,
'email' => FILTER_VALIDATE_EMAIL,
'ip' => FILTER_VALIDATE_IP,
'url' => FILTER_VALIDATE_URL,
);
}
if (!isset($filterTypes[$type]))
{
trigger_error("Unsupported type '" . $type . "' used in e107::getParser()->filter().", E_USER_WARNING);
}
$filter = $filterTypes[$type];
$filter = function($element) use ($filter)
{
$element = (string) $element;
return is_callable($filter) ? $filter($element) : filter_var($element, $filter);
};
if (is_array($text))
{
$ret = filter_var($text, FILTER_CALLBACK, ['options' => $filter]);
}
else
{
$ret = $filter($text);
}
}
return $ret;
}
/**
* @return void
*/
private function grantScriptAccess()
{
if (!in_array('script', $this->allowedTags))
{
$this->allowedTags = array_merge($this->allowedTags, $this->scriptTags);
}
foreach ($this->allowedAttributes as $tag => $att)
{
foreach ($this->scriptAttributes as $new)
{
if (in_array($new, $this->allowedAttributes[$tag]))
{
continue;
}
$this->allowedAttributes[$tag][] = $new;
}
}
}
/**
* Process and clean HTML from user input.
* TODO Html5 tag support.
*
* @param string $html raw HTML
* @param boolean $checkPref
* @return string
*/
public function cleanHtml($html = '', $checkPref = true)
{
if (empty($html))
{
return '';
}
if ($this->isHtml($html) === false)
{
$html = str_replace(array('<', '>'), array('&lt;', '&gt;'), $html);
}
// prevent replacement of &nbsp; with spaces.
// Workaround for https://bugs.php.net/bug.php?id=76285
// Part 1 of 2
// clean out windows line-breaks.
$html = str_replace(array('&nbsp;', "\r", "\n", '{', '}'), array('__E_PARSER_CLEAN_HTML_NON_BREAKING_SPACE__', '', '__E_PARSER_CLEAN_HTML_LINE_BREAK__', '__E_PARSER_CLEAN_HTML_CURLY_OPEN__', '__E_PARSER_CLEAN_HTML_CURLY_CLOSED__'), $html);
if (strpos($html, '<body') === false) // HTML Fragment
{
$html = '<body>' . $html . '</body>';
}
if ($this->scriptAccess === false)
{
$this->scriptAccess = e107::getConfig()->get('post_script', e_UC_NOBODY); // Pref to Allow <script> tags11;
}
if (check_class($this->scriptAccess))
{
$this->grantScriptAccess();
}
elseif(deftrue('ADMIN') && (strpos($html, '</script>') !== false))
{
$lan1 = defset('LAN_NO_SCRIPT_ACCESS', "You don't have permission to use [script] tags.");
$lan2 = defset('', "If you believe this is an error, please ask the main administrator to grant you script access via [b]Preferences > Content Filters[/b]");
$srch = ['[', ']'];
$repl = ['<b>&lt;', '&gt;</b>'];
e107::getMessage()->addWarning(str_replace($srch,$repl,$lan1));
e107::getMessage()->addWarning(e107::getParser()->toHTML($lan2,true));
}
// Set it up for processing.
libxml_use_internal_errors(true);
if (function_exists('mb_convert_encoding'))
{
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
}
// $fragment = $doc->createDocumentFragment();
// $fragment->appendXML($html);
// $doc->appendChild($fragment);
// $doc->encoding = 'utf-8';
$doc = $this->domObj;
$opts = defined('LIBXML_HTML_NOIMPLIED') ? LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD : 0;
$doc->loadHTML($html, $opts);
$this->nodesToConvert = array(); // required.
$this->nodesToDelete = array(); // required.
$this->removedList = array();
$tmp = $doc->getElementsByTagName('*');
/** @var DOMElement $node */
foreach ($tmp as $node)
{
$path = $node->getNodePath();
// echo "<br />Path = ".$path;
// $tag = strval(basename($path));
if (strpos($path, '/code') !== false || strpos($path, '/pre') !== false) // treat as html.
{
$this->pathList[] = $path;
// $this->nodesToConvert[] = $node->parentNode; // $node;
$this->nodesToDisableSC[] = $node;
continue;
}
$tag = preg_replace('/([a-z0-9\[\]\/]*)?\/([\w\-]*)(\[(\d)*\])?$/i', '$2', $path);
if (!in_array($tag, $this->allowedTags))
{
$this->removedList['tags'][] = $tag;
$this->nodesToDelete[] = $node;
continue;
}
$removeAttributes = array();
foreach ($node->attributes as $attr)
{
$name = $attr->nodeName;
$value = $attr->nodeValue;
$allow = isset($this->allowedAttributes[$tag]) ? $this->allowedAttributes[$tag] : $this->allowedAttributes['default'];
if (!in_array($name, $allow))
{
if ($this->scriptAccess == true && strpos($name, 'data-') === 0)
{
continue;
}
$removeAttributes[] = $name;
//$node->removeAttribute($name);
$this->removedList['attributes'][] = $name . ' from <' . $tag . '>';
continue;
}
if ($this->invalidAttributeValue($value)) // Check value against blacklisted values.
{
//$node->removeAttribute($name);
$node->setAttribute($name, '#---sanitized---#');
$this->removedList['sanitized'][] = $tag . '[' . $name . ']';
}
else
{
$_value = $this->secureAttributeValue($name, $value);
$node->setAttribute($name, $_value);
if ($_value !== $value)
{
$this->removedList['sanitized'][] = $tag . '[' . $name . '] converted "' . $value . '" -> "' . $_value . '"';
}
}
}
// required - removing attributes in a loop breaks the loop
if (!empty($removeAttributes))
{
foreach ($removeAttributes as $name)
{
$node->removeAttribute($name);
}
}
}
// Remove some stuff.
foreach ($this->nodesToDelete as $node)
{
$node->parentNode->removeChild($node);
}
// Disable Shortcodes in pre/code
foreach ($this->nodesToDisableSC as $key => $node)
{
$value = $node->C14N();
if (empty($value))
{
continue;
}
$value = str_replace('&#xD;', "\r", $value);
if ($node->nodeName === 'pre')
{
$value = preg_replace('/^<pre[^>]*>/', '', $value);
$value = str_replace(array('</pre>', '<br></br>'), array('', '__E_PARSER_CLEAN_HTML_LINE_BREAK__'), $value);
}
elseif ($node->nodeName === 'code')
{
$value = preg_replace('/^<code[^>]*>/', '', $value);
$value = str_replace(array('</code>', '<br></br>'), array('', '__E_PARSER_CLEAN_HTML_LINE_BREAK__'), $value);
}
// temporarily change {e_XXX} to {{{e_XXX}}}
$value = str_replace(array('__E_PARSER_CLEAN_HTML_CURLY_OPEN__', '__E_PARSER_CLEAN_HTML_CURLY_CLOSED__'), array('{{{', '}}}'), $value); // temporarily change {e_XXX} to {{{e_XXX}}}
$newNode = $doc->createElement($node->nodeName);
$newNode->nodeValue = $value;
if ($class = $node->getAttribute('class'))
{
$newNode->setAttribute('class', $class);
}
if ($style = $node->getAttribute('style'))
{
$newNode->setAttribute('style', $style);
}
$node->parentNode->replaceChild($newNode, $node);
}
// Convert <code> and <pre> Tags to Htmlentities.
/* TODO XXX Still necessary? Perhaps using bbcodes only?
foreach($this->nodesToConvert as $node)
{
$value = $node->C14N();
$value = str_replace("&#xD;","",$value);
// print_a("WOWOWO");
if($node->nodeName == 'pre')
{
$value = substr($value,5);
$end = strrpos($value,"</pre>");
$value = substr($value,0,$end);
}
if($node->nodeName == 'code')
{
$value = substr($value,6);
$end = strrpos($value,"</code>");
$value = substr($value,0,$end);
}
$value = htmlentities(htmlentities($value)); // Needed
$node->nodeValue = $value;
}
*/
$cleaned = $doc->saveHTML($doc->documentElement); // $doc->documentElement fixes utf-8/entities issue. @see http://stackoverflow.com/questions/8218230/php-domdocument-loadhtml-not-encoding-utf-8-correctly
$cleaned = str_replace(
array("\n", '__E_PARSER_CLEAN_HTML_LINE_BREAK__', '__E_PARSER_CLEAN_HTML_NON_BREAKING_SPACE__', '{{{', '}}}', '__E_PARSER_CLEAN_HTML_CURLY_OPEN__', '__E_PARSER_CLEAN_HTML_CURLY_CLOSED__', '<body>', '</body>', '<html>', '</html>'),
array('', "\n", '&nbsp;', '&#123;', '&#125;', '{', '}', '', '', '', ''),
$cleaned
); // filter out tags.
return trim($cleaned);
}
/**
* @param $attribute
* @param $value
* @return array|mixed|string|string[]
*/
public function secureAttributeValue($attribute, $value)
{
$search = isset($this->replaceAttrValues[$attribute]) ? $this->replaceAttrValues[$attribute] : $this->replaceAttrValues['default'];
if (!empty($search))
{
$value = str_replace($search, '', $value);
}
return $value;
}
/**
* Check for Invalid Attribute Values
*
* @param $value string
* @return bool true/false
*/
public function invalidAttributeValue($value)
{
foreach ($this->badAttrValues as $v) // global list because a bad value is bad regardless of the attribute it's in. ;-)
{
if (preg_match('/' . $v . '/i', $value) == true)
{
$this->removedList['blacklist'][] = "Match found for '{$v}' in '{$value}'";
return true;
}
}
return false;
}
/**
* @param $modifiers
* @return array
*/
private function getModifiers($modifiers)
{
$opts = $this->e_optDefault;
if (strpos($modifiers, 'defaults_off') !== false)
{
$opts = $this->e_SuperMods['NODEFAULT'];
}
// Now process any modifiers that are specified
$aMods = explode(',', $modifiers);
// If there's a supermodifier, it must be first, and in uppercase
$psm = trim($aMods[0]);
if (isset($this->e_SuperMods[$psm]))
{
// Supermodifier found - override default values where necessary
$opts = array_merge($opts, $this->e_SuperMods[$psm]);
$opts['context'] = $psm;
unset($aMods[0]);
}
// Now find any regular modifiers; use them to modify the context
// (there should only be one or two out of the list of possibles)
foreach ($aMods as $mod)
{
// Slight concession to varying coding styles - stripping spaces is a waste of CPU cycles!
$mod = trim($mod);
if (isset($this->e_Modifiers[$mod]))
{
// This is probably quicker than array_merge
// - especially as usually only one or two loops
foreach ($this->e_Modifiers[$mod] as $k => $v)
{
// Update our context-specific options
$opts[$k] = $v;
}
}
}
// Turn off a few things if not enabled in options
if (empty($this->pref['smiley_activate']))
{
$opts['emotes'] = false;
}
if (empty($this->pref['make_clickable']))
{
$opts['link_click'] = false;
}
if (empty($this->pref['link_replace']))
{
$opts['link_replace'] = false;
}
return $opts;
}
/**
* @param array $opts
* @param string $text
* @param bool $convertNL
* @param bool|string $parseBB
* @param $modifiers
* @param int $postID
* @return array|bool|mixed|string|null
*/
private function processModifiers($opts, $text, $convertNL, $parseBB, $modifiers, $postID)
{
if ($opts['link_click'])
{
if ($opts['link_replace'] && defset('ADMIN_AREA') !== true)
{
$link_text = $this->pref['link_text'];
$email_text = ($this->pref['email_text']) ? $this->replaceConstants($this->pref['email_text']) : LAN_EMAIL_SUBS;
$text = $this->makeClickable($text, 'url', array('sub' => $link_text, 'ext' => $this->pref['links_new_window']));
$text = $this->makeClickable($text, 'email', array('sub' => $email_text));
}
else
{
$text = $this->makeClickable($text, 'url', array('ext' => true));
$text = $this->makeClickable($text, 'email');
}
}
// Convert emoticons to graphical icons, if enabled
if ($opts['emotes'])
{
$text = e107::getEmote()->filterEmotes($text);
}
// Reduce newlines in all forms to a single newline character (finds '\n', '\r\n', '\n\r')
if (!$opts['nobreak'])
{
if ($convertNL && ($this->preformatted($text) === false)) // eg. html or markdown
{
// We may need to convert to <br /> later
$text = preg_replace("#[\r]*\n[\r]*#", E_NL, $text);
}
else
{
// Not doing any more - its HTML or Markdown so keep it as is.
$text = preg_replace("#[\r]*\n[\r]*#", "\n", $text);
}
}
// Entity conversion
// Restore entity form of quotes and such to single characters, except for text destined for tag attributes or JS.
if ($opts['value'])
{
// output used for attribute values.
$text = str_replace($this->replace, $this->search, $text);
}
else
{
// output not used for attribute values.
$text = str_replace($this->search, $this->replace, $text);
}
// BBCode processing (other than the four already done, which shouldn't appear at all in the text)
if ($parseBB !== false)
{
if ($parseBB === true)
{
// 'Normal' or 'legacy' processing
if ($modifiers === 'WYSIWYG')
{
$text = e107::getBB()->parseBBCodes($text, $postID, 'wysiwyg');
}
else
{
$text = e107::getBB()->parseBBCodes($text, $postID);
}
}
elseif ($parseBB === 'STRIP') // Need to strip all BBCodes
{
$text = e107::getBB()->parseBBCodes($text, $postID, 'default', true);
}
else // Need to strip just some BBCodes
{
$text = e107::getBB()->parseBBCodes($text, $postID, 'default', $parseBB);
}
}
// replace all {e_XXX} constants with their e107 value. modifier determines relative/absolute conversion
// (Moved to after bbcode processing by Cameron)
if ($opts['constants'])
{
$text = $this->replaceConstants($text, $opts['constants']); // Now decodes text values
}
// profanity filter
if ($this->pref['profanity_filter'])
{
$text = e107::getProfanity()->filterProfanities($text);
}
// Optional short-code conversion
if ($opts['parse_sc'])
{
$text = $this->parseTemplate($text, true);
}
/**
* / @deprecated
*/
if ($opts['hook']) //Run any hooked in parsers
{
if (!empty($this->pref['tohtml_hook']))
{
// trigger_error('<b>tohtml_hook is deprecated.</b> Use e_parse.php instead.', E_USER_DEPRECATED); // NO LAN
//Process the older tohtml_hook pref (deprecated)
foreach (explode(',', $this->pref['tohtml_hook']) as $hook)
{
if (!is_object($this->e_hook[$hook]) && is_readable(e_PLUGIN . $hook . '/' . $hook . '.php'))
{
require_once(e_PLUGIN . $hook . '/' . $hook . '.php');
$hook_class = 'e_' . $hook;
$this->e_hook[$hook] = new $hook_class;
}
if (is_object($this->e_hook[$hook])) // precaution for old plugins.
{
$text = $this->e_hook[$hook]->$hook($text, $opts['context']);
}
}
}
/**
* / @deprecated
*/
if (isset($this->pref['e_tohtml_list']) && is_array($this->pref['e_tohtml_list']))
{
foreach ($this->pref['e_tohtml_list'] as $hook)
{
if (empty($hook))
{
continue;
}
if (empty($this->e_hook[$hook]) && is_readable(e_PLUGIN . $hook . '/e_tohtml.php') /*&& !is_object($this->e_hook[$hook])*/)
{
require_once(e_PLUGIN . $hook . '/e_tohtml.php');
$hook_class = 'e_tohtml_' . $hook;
if (class_exists($hook_class))
{
$this->e_hook[$hook] = new $hook_class;
}
}
if (isset($this->e_hook[$hook]) && is_object($this->e_hook[$hook]))
{
/** @var e_tohtml_linkwords $deprecatedHook */
$deprecatedHook = $this->e_hook[$hook];
$text = $deprecatedHook->to_html($text, $opts['context']);
}
}
}
/**
* / Preferred 'hook'
*/
if (!empty($this->pref['e_parse_list']))
{
foreach ($this->pref['e_parse_list'] as $plugin)
{
$hookObj = e107::getAddon($plugin, 'e_parse');
if ($tmp = e107::callMethod($hookObj, 'toHTML', $text, $opts['context']))
{
$text = $tmp;
}
}
}
}
// Word wrap
if (!empty($this->pref['main_wordwrap']) && !$opts['nobreak'])
{
$text = $this->textclean($text, $this->pref['main_wordwrap']);
}
// Search highlighting
if ($opts['emotes'] && $this->checkHighlighting()) // Why??
{
$text = $this->e_highlight($text, $this->e_query);
}
if ($convertNL == true)
{
// Default replaces all \n with <br /> for HTML display
$nl_replace = '<br />';
if ($opts['nobreak'])
{
$nl_replace = '';
}
elseif ($opts['retain_nl'])
{
$nl_replace = "\n";
}
$text = str_replace(E_NL, $nl_replace, $text);
}
return $text;
}
}