rror', '>', '"', ' & '); // 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; /** @var array Stored relative paths - used by replaceConstants() */ private $relativePaths = array(); // 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
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
'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 $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. /** * 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(); } 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(['"', "'"], ['"', '''], $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('&','&',$data); // $data = str_replace('<','<',$data); // $data = str_replace('>','>',$data); // $data = str_replace('&','&',$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('$', '"', "'", '\\', '', '<'); $data = str_replace($search, $replace, $data); // prevent &gt; etc. $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); $data = str_replace('\\', '\', $data); $ret = preg_replace("/&#(\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", "[\\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('' . __METHOD__ . ' is deprecated. Use $tp->cleanHtml() instead.', 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; } 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); } 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('"', ''', '\', '&',); // '&' must be last. $replace = array('"', "'", "\\", '&'); // 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('$', '"', '<', '>', '+'); $replace = array('$', '"', '<', '>', '%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(" ", " ", $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(''', '"', '<', '>'), $text); } 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(''; // markers for html-to-bbcode replacement. $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 tags. $opts['nobreak'] = true; $parseBB = false; // prevent further bbcode processing. break; case 'table' : // strip
from inside of $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('#((?:]+>.*?|tyle[^>]+>.*?))#mis', $full_text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); foreach ($subcon as $sub_blk) { if (strpos($sub_blk, '"; } $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
on block-level elements (eg. div, pre, table, etc. ) $srch = array(); $repl = array(); foreach ($this->blockTags as $val) { $srch[] = '
'; $repl[] = ''; } $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; } 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', ); 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 "Hello"; */ public function toAttribute($text, $pure = false) { // URLs posted without HTML access may have an & 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. <a onclick="ATTRIBUTE"></a>), surround the string with * {@see e_parse::toAttribute()} and either single-quote or double-quote the attribute value. */ public function toJS($stringarray) { trigger_error('' . __METHOD__ . ' is deprecated. See method DocBlock for alternatives.', E_USER_WARNING); // NO LAN $search = array("\r\n", "\r", '
', "'"); $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('&#039;', '&#036;', ''', '$', e_BASE, "href='request.php", '', ''); $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', ']]>'], $text); if ($tags === true) { $text = !empty($text) ? '' : ''; } else { $text = str_replace(['<', '>'], ['<', '>'], $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 preg_replace('/[^-0-9]/', '', $value); } return ( 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('&', '&'), array('&', '&'), $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('
', '
', '
'), "\n", $text); $text = strip_tags($text); } $search = array('&#039;', '&#036;', ''', '$', '\', '&#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; } } 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; } /** * 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' => (string) intval($options['w']), 'h' => (string) intval($options['h']), 'aw' => (string) intval($options['aw']), '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; } 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; if (is_array($staticArray)) { $cnt = count($staticArray); $staticCount = $this->staticCount(); if ($staticCount > ($cnt - 1)) { $staticCount = 0; $this->staticCount(0); } $http = !empty($staticArray[$staticCount]) ? $staticArray[$staticCount] : e_HTTP; } else { $http = $this->staticUrl; } $this->staticCount(1); if (empty($path)) { return $http; } $base = ''; $path = $this->replaceConstants($path, 'abs'); // replace any {THEME} etc. $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; } /** * 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} * @param int $options ['w'] width (optional) * @param int $options ['h'] height (optional) * @param bool|string $options ['crop'] true/false or A(auto) or T(op) or B(ottom) or C(enter) or L(eft) or R(right) * @param string $options ['scale'] '2x' (optional) * @param bool $options ['x'] encode/mask the url parms (optional) * @param bool $options ['nosef'] 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) . '&'; // 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'] . '&ah=' . (int) $options['h']; if (!is_numeric($options['crop'])) { $thurl .= '&c=' . $options['crop']; $options['nosef'] = true; } } else { $thurl .= 'w=' . (int) $options['w'] . '&h=' . (int) $options['h']; } if (!empty($options['type'])) { $thurl .= '&type=' . $options['type']; } if (defined('e_MEDIA_STATIC')) // experimental - subject to change. { $opts = str_replace('&', '&', $thurl); $staticFile = $this->thumbCacheFile($url, $opts); if (!empty($staticFile) && is_readable(e_CACHE_IMAGE . $staticFile)) { return $this->staticUrl(e_CACHE_IMAGE_ABS . $staticFile); } // echo "
static-not-found: ".$staticFile; $options['nosef'] = true; $options['x'] = null; // file_put_contents(e_LOG."thumb.log", "\n++++++++++++++++++++++++++++++++++\n\n", FILE_APPEND); } if (e_MOD_REWRITE_MEDIA == true && empty($options['nosef']))// Experimental SEF URL support. { $options['full'] = $full; $options['ext'] = substr($url, -3); $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) : (intval($this->thumbWidth) * $multiInt); $height = isset($parm['h']) ? (intval($parm['h']) * $multiInt) : (intval($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; } public function thumbUrlScale($src, $parm) { } /** * 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(); } 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'), (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 : '', (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; } 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? public function e_highlight($text, $match) { $tags = array(); preg_match_all('#<[^>]+>#', $text, $tags); $text = preg_replace('#<[^>]+>#', '<|>', $text); $text = preg_replace('#(\b".$match."\b)#i', '\\1', $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', '[\\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 = '@'; $user = "data-user='" . $this->obfuscate($name) . "'"; $dom = "data-dom='" . $this->obfuscate($address) . "'"; } else { $user = ''; $dom = ''; } $url = 'mailto:' . $email . $subject; $safe = $this->obfuscate($url); return "" . $words . ''; } /** * 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; } 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; } 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; } public function setScriptAttibutes($arr) { $this->scriptAttributes = (array) $arr; } public function getAllowedTags() { return $this->allowedTags; } public function getAllowedAttributes() { return $this->allowedAttributes; } public function getScriptAccess() { return $this->scriptAccess; } 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; } /** * 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) ? '' . $v . '' : $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 . ''; } $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(' ', '', $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 array|false|string|string[]|void */ 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('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. 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 ""; 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) { $fab = e107::getMedia()->getGlyphs('fab'); $fas = e107::getMedia()->getGlyphs('fas'); $far = e107::getMedia()->getGlyphs('far'); $shims = e107::getMedia()->getGlyphs('fa5-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 '; $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']; 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 . ''; $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 "" . $text . ''; } /** * 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[] = "" . $v . ''; } 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 "-attachment-"; //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 * @param int $options ['w'] - image width in px * @param int $options ['h'] - image height in px * @param int|bool $options ['crop'] = enables cropping when true * @param string $options ['shape'] - (optional) rounded|circle|thumbnail * @param string $options ['id'] - 'id' attribute will be added to tag. * @param string $options ['class'] - override default 'class' attribute in tag. * @param string $options ['alt'] - override default 'alt' attribute in tag. * @param bool $options ['base64'] - use embedded base64 for image src. * @param bool $options ['hd'] - double the resolution of the image. Useful for retina displays. * @param string $options ['type'] - when set to 'url' returns the URL value instead of the tag. * @param string $options ['style'] - sets the style attribute. * @param string $options ['mode'] - 'full' url mode. * @return string 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); // returns false during unit tests, works otherwise. if (!empty($content)) { $ext = strtolower(pathinfo($file, 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 = ""; $linkEnd = ''; } $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 .= '\""'; $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 "
Use \$tp->toImage() instead of toIcon() for ".$icon."
"; // debug info only. // } if (strpos($icon, '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 "" . $alt . "'; } /** * Render an img tag. * * @param string $file * @param array $parm keys: legacy|w|h|alt|class|id|crop|loading * @param array $parm ['legacy'] Usually a legacy path like {e_FILE} * @param array $parm ['type'] 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 "
Use \$tp->toAvatar() instead of toImage() for " . $file . '
'; // 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; } } /** @var e_parse $tp */ $tp = $this; // 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'])) // 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 = "\n"; if(!empty($parm['srcset'])) { list($webPSourceSet, $webPSize) = explode(' ', $parm['srcset']); $html .= ''; $html .= "\n"; $html .= ''; $html .= "\n"; $srcset = ''; // remove it from the img tag below. } $html .= ''; $html .= "\n"; }*/ if (empty($path)) { return null; } $html .= "' . $alt . ''; // $html .= ($this->convertToWebP) ? "\n" : ''; 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'); $mime = varset($parm['mime'], 'audio/mpeg'); $autoplay = !empty($parm['autoplay']) ? 'autoplay ' : ''; $controls = !empty($parm['controls']) ? 'controls' : ''; $text = ''; return $text; } /** * Display a Video file. * * @param string $file - format: id.type eg. x123dkax.youtube * @param boolean $thumbnail - 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, '', '&'); $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 = ''; $url = 'http://youtu.be/' . $id; if ($mode === 'url') { return $url; } if ($thumb === 'tag') { return "Youtube Video"; } 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 "" . LAN_YOUTUBE_VIDEO . "
" . LAN_CLICK_TO_VIEW . '
'; } if ($thumb === 'src') { return $thumbSrc; } if ($thumb === 'video') { return '
' . $video . '
'; } return '
' . $video . '
'; } if ($type === 'youtubepl') { if ($thumb === 'tag') { $thumbSrc = e107::getMedia()->getThumb($id); if (empty($thumbSrc)) { $thumbSrc = e_IMAGE_ABS . 'generic/playlist_120.png'; } return "" . LAN_YOUTUBE_PLAYLIST . ""; } 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 = ''; return '
' . $video . '
'; } 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'); return '
'; } 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 '' . $value . ''; } /** * Parse new tags into bbcode output. * * @param bool $retainTags : when you want to replace html and retain the tags wrapping it. * @return string html */ public function parseBBTags($text, $retainTags = false) { $stext = str_replace('"', '"', $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>)/i',$repl, $text); $text = preg_replace('/().*(<\/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_\.]/', ); 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; } 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; } } return null; } /** * 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('<', '>'), $html); } // prevent replacement of   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(' ', "\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, ''; } if ($this->scriptAccess === false) { $this->scriptAccess = e107::getConfig()->get('post_script', e_UC_NOBODY); // Pref to Allow ') !== 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 = ['<', '>']; 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 "
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(' ', "\r", $value); if ($node->nodeName === 'pre') { $value = preg_replace('/^]*>/', '', $value); $value = str_replace(array('', '

'), array('', '__E_PARSER_CLEAN_HTML_LINE_BREAK__'), $value); } elseif ($node->nodeName === 'code') { $value = preg_replace('/^]*>/', '', $value); $value = str_replace(array('', '

'), 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 and
 Tags to Htmlentities.
		/* TODO XXX Still necessary? Perhaps using bbcodes only?
		foreach($this->nodesToConvert as $node)
		{
			$value = $node->C14N();

			$value = str_replace("
","",$value);

		//    print_a("WOWOWO");

			if($node->nodeName == 'pre')
			{
				$value = substr($value,5);
				$end = strrpos($value,"
"); $value = substr($value,0,$end); } if($node->nodeName == 'code') { $value = substr($value,6); $end = strrpos($value,"
"); $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__', '', '', '', ''), array('', "\n", ' ', '{', '}', '{', '}', '', '', '', ''), $cleaned ); // filter out tags. return trim($cleaned); } 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
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('tohtml_hook is deprecated. 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
for HTML display $nl_replace = '
'; if ($opts['nobreak']) { $nl_replace = ''; } elseif ($opts['retain_nl']) { $nl_replace = "\n"; } $text = str_replace(E_NL, $nl_replace, $text); } return $text; } }