/** * Get the text with all the smilie codes replaced with the correct XHTML. Smiles are determined by your forum system. * This is not used in the normal comcode chain - it's for non-comcode things that require smilies (actually in reality it is used in the Comcode chain if the optimiser sees that a full parse is not needed) * * @param string The text to add smilies to (assumption: that this is XHTML) * @return string The XHTML with the image-substitution of smilies */ function apply_emoticons($text) { $_smilies = $GLOBALS['FORUM_DRIVER']->find_emoticons(); // Sorted in descending length order if ($GLOBALS['XSS_DETECT']) { $orig_escaped = ocp_is_escaped($text); } // Pre-check, optimisation $smilies = array(); foreach ($_smilies as $code => $imgcode) { if (strpos($text, $code) !== false) { $smilies[$code] = $imgcode; } } if (count($smilies) != 0) { $len = strlen($text); for ($i = 0; $i < $len; ++$i) { $char = $text[$i]; if ($char == '"') { $i++; continue; } foreach ($smilies as $code => $imgcode) { $code_len = strlen($code); if ($char == $code[0] && substr($text, $i, $code_len) == $code) { $eval = do_emoticon($imgcode); $_eval = $eval->evaluate(); if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($_eval); } $before = substr($text, 0, $i); $after = substr($text, $i + $code_len); if ($before == '' && $after == '') { $text = $_eval; } else { $text = $before . $_eval . $after; } $len = strlen($text); $i += strlen($_eval) - 1; break; } } } if ($GLOBALS['XSS_DETECT'] && $orig_escaped) { ocp_mark_as_escaped($text); } } return $text; }
/** * Convert a language string into another language string. * * @param mixed The string to convert * @param LONG_TEXT The language to convert to * @return LONG_TEXT The converted string */ function google_translate($str_in, $lang) { $tempcode = is_object($str_in); $GLOBALS['NO_QUERY_LIMIT'] = true; if (get_option('enable_google_translate', true) !== '1') { return $str_in; } if ($tempcode) { $str_in = $str_in->evaluate(); } global $DOING_TRANSLATE; if (!isset($DOING_TRANSLATE)) { $DOING_TRANSLATE = false; } if ($DOING_TRANSLATE) { return $tempcode ? protect_from_escaping($str_in) : $str_in; } // Don't want loops if ($str_in == '') { return $tempcode ? protect_from_escaping(escape_html('')) : escape_html(''); } if (strpos($str_in, 'gtranslate_cache') !== false) { return $tempcode ? protect_from_escaping($str_in) : $str_in; } // Stop loops about corrupt/missing database tables $language_list = array('ar' => 'Arabic', 'bg' => 'Bulgarian', 'zh-cn' => 'Simplified Chinese', 'zh-tw' => 'Traditional Chinese', 'hr' => 'Croatian', 'cs' => 'Czech', 'da' => 'Danish', 'nl' => 'Dutch', 'en' => 'English', 'fi' => 'Finnish', 'fr' => 'French', 'de' => 'German', 'el' => 'Greek', 'hi' => 'Hindi', 'it' => 'Italian', 'ja' => 'Japanese', 'ko' => 'Korean', 'pl' => 'Polish', 'pt' => 'Portuguese', 'ro' => 'Romanian', 'ru' => 'Russian', 'es' => 'Spanish', 'sv' => 'Swedish'); $lang = strtolower($lang); if (!array_key_exists($lang, $language_list)) { return $tempcode ? protect_from_escaping($str_in) : $str_in; } $DOING_TRANSLATE = true; require_lang('lang'); $chache = check_google_cache($str_in, $lang); if (count($chache) == 0) { require_code('GTranslate'); $translate = new GTranslate(); $num_matches = array(); $matches = array(); $rep = array(); $prepped = $str_in; $j = 0; foreach (array(array('[', ']'), array('{', '}')) as $symbol) { $_matches = array(); $_num_matches = preg_match_all('#[' . preg_quote($symbol[0]) . '][^' . preg_quote($symbol[0]) . preg_quote($symbol[1]) . ']*[' . preg_quote($symbol[1]) . ']#', $str_in, $_matches); $matches[$symbol[0]] = $_matches; $num_matches[$symbol[0]] = $_num_matches; for ($i = 0; $i < $_num_matches; $i++) { $from = $_matches[0][$i]; $to = '<span class="notranslate">' . strval($j) . '</span>'; $rep['!' . strval($j)] = $from; // The '!' bit is because we can't trust indexing in PHP arrays if it is numeric $pos = 0; do { $pos = strpos($prepped, $from, $pos); if ($pos !== false) { $pos_open = strrpos(substr($prepped, 0, $pos), '<'); $pos_close = strrpos(substr($prepped, 0, $pos), '>'); if ($pos_open === false || $pos_close !== false && $pos_close > $pos_open) { $prepped = substr($prepped, 0, $pos) . $to . substr($prepped, $pos + strlen($from)); $pos += strlen($to); } else { $pos_title = strrpos(substr($prepped, 0, $pos), 'title="'); $pos_alt = strrpos(substr($prepped, 0, $pos), 'alt="'); $pos_quote = strrpos(substr($prepped, 0, $pos), '"'); if ($pos_alt !== false && $pos_alt > $pos_open && $pos_quote == $pos_alt + 4 || $pos_title !== false && $pos_title > $pos_open && $pos_quote == $pos_title + 6) { $to2 = ' conv' . strval($j) . ' '; $prepped = substr($prepped, 0, $pos) . $to2 . substr($prepped, $pos + strlen($from)); $pos += strlen($to2); } else { $pos += strlen($from); } } } } while ($pos !== false); $j++; } } if (strpos(preg_replace('#<[^>]*>#', '', $prepped), '{') !== false) { $DOING_TRANSLATE = false; return $tempcode ? protect_from_escaping($str_in) : $str_in; // Cannot translate as it has very complex Tempcode in it } $to = $language_list[$lang]; $from_lang = strtolower(get_site_default_lang()); try { $convertedstring = $translate->Text($prepped)->From(array_key_exists($from_lang, $language_list) ? $language_list[$from_lang] : 'English')->To($to); } catch (Exception $e) { } if ($convertedstring === NULL) { $convertedstring = $str_in; } do { $before = $convertedstring; $convertedstring = preg_replace('#(<span class="notranslate">\\d+) (.*</span>)#', '${1}</span> <span class="notranslate">${2}', $convertedstring); } while ($before != $convertedstring); foreach (array_reverse($rep) as $_j => $from) { $j = intval(substr($_j, 1)); $convertedstring = preg_replace('#\\s*<span class="notranslate">\\s*' . preg_quote(strval($j)) . '\\s*</span>\\s*#', $from, $convertedstring); $convertedstring = preg_replace('# conv' . preg_quote(strval($j)) . '\\s*#', $from, $convertedstring); } $convertedstring = str_replace('<html> ', '', $convertedstring); $convertedstring = str_replace(''', '', $convertedstring); save_google_cache($str_in, $lang, $convertedstring); $str = $convertedstring; } else { $str = $chache['t_result']; } $DOING_TRANSLATE = false; if (function_exists('ocp_mark_as_escaped') && ocp_is_escaped($str_in)) { ocp_mark_as_escaped($str); } return $tempcode ? protect_from_escaping($str) : $str; }
/** * Function that 'fixes' HTML (or bad XHTML) enough for it to pass most basic structural validation. * * @param string The XHTML string to convert to XHTML * @param boolean Whether to force a repair even if we aren't in XHTML mode * @return string The converted string */ function xhtmlise_html($html, $definitely_want = false) { // Tests... // echo xhtmlise_html('test<a></a><br /><po></p><p></po>'); // expect: test<a></a><br /><po><p></p></po> if (!$definitely_want) { if (!($GLOBALS['SEMI_DEBUG_MODE'] && browser_matches('true_xhtml'))) { return $html; } // One day, this will get removed and we'll ensure all our output is always XHTML. But so far there's no point as IE doesn't support true XHTML } $is_escaped = $GLOBALS['XSS_DETECT'] && ocp_is_escaped($html); $html = preg_replace('#<\\!--.*($|-->)#Us', '', $html); // Strip comments require_code('obfuscate'); require_code('validation'); global $XML_CONSTRAIN, $LAST_TAG_ATTRIBUTES, $POS, $OUT, $TAG_STACK, $INBETWEEN_TEXT, $LEN, $WELL_FORMED_ONLY, $MUST_SELFCLOSE_TAGS, $LINENO, $LINESTART; $POS = 0; $OUT = $html; $LEN = strlen($html); $TAG_STACK = array(); $WELL_FORMED_ONLY = true; $LINENO = 0; $LINESTART = 0; $XML_CONSTRAIN = true; $new = ''; $token = _get_next_tag(); // If we actually have a partial tag right at the start (ie. we're breaking into some HTML at a bad point) $ang_pos = strpos($INBETWEEN_TEXT, '>'); if ($ang_pos !== false) { $INBETWEEN_TEXT = substr($INBETWEEN_TEXT, $ang_pos + 1); } $new .= fix_entities($INBETWEEN_TEXT); while (!is_null($token)) { while (is_array($token)) { if ($token[0] !== NULL) { $token = $token[0]; // We can at least discern something } else { $token = _get_next_tag(); // No, we need to just move on } } $basis_token = _get_tag_basis($token); if ($basis_token != '') { // Open, close, or monitonic? $term = strpos($token, '/'); if ($term !== 1) { if ($term === false && !isset($MUST_SELFCLOSE_TAGS[$basis_token])) { // Fix nesting if ($basis_token == 'li' && !in_array('ul', $TAG_STACK) && !in_array('ol', $TAG_STACK) && !in_array('dl', $TAG_STACK) && !in_array('dd', $TAG_STACK) && !in_array('dt', $TAG_STACK) && !in_array('dir', $TAG_STACK) && !in_array('menu', $TAG_STACK)) { array_push($TAG_STACK, 'ul'); $new .= '<ul>'; } if (($basis_token == 'tr' || $basis_token == 'colgroup' || $basis_token == 'col' || $basis_token == 'tbody' || $basis_token == 'tfoot' || $basis_token == 'thead' || $basis_token == 'caption') && !in_array('table', $TAG_STACK)) { array_push($TAG_STACK, 'table'); $new .= '<table>'; } if (($basis_token == 'td' || $basis_token == 'th') && !in_array('table', $TAG_STACK)) { array_push($TAG_STACK, 'table'); $new .= '<table>'; array_push($TAG_STACK, 'tr'); $new .= '<tr>'; } if ($basis_token == 'param' && !in_array('object', $TAG_STACK)) { array_push($TAG_STACK, 'object'); $new .= '<object>'; } if ($basis_token == 'option' && !in_array('select', $TAG_STACK)) { array_push($TAG_STACK, 'select'); $new .= '<select>'; } if ($basis_token == 'noembed' && !in_array('map', $TAG_STACK)) { array_push($TAG_STACK, 'map'); $new .= '<map>'; } array_push($TAG_STACK, $basis_token); $new .= '<' . $basis_token; foreach ($LAST_TAG_ATTRIBUTES as $key => $val) { $new .= ' ' . $key . '="' . fix_entities($val) . '"'; } $new .= '>'; } else { $new .= '<' . $basis_token; foreach ($LAST_TAG_ATTRIBUTES as $key => $val) { $new .= ' ' . $key . '="' . fix_entities($val) . '"'; } $new .= ' />'; } } else { // For case 3 if (!in_array($basis_token, $TAG_STACK)) { // Do nothing, we can't handle it because we're closing something that was never opened } else { $previous = ''; do { $previous = array_pop($TAG_STACK); if ($basis_token != $previous) { $new .= '</' . $previous . '>'; } // We'll have to assume it should be implicitly closed } while ($basis_token != $previous); $new .= '</' . $basis_token . '>'; // Ok so we finally got an opener match and managed to put out our closer } } } $token = _get_next_tag(); if (is_null($token)) { // If we actually have a partial tag right at the end (ie. we're breaking out of some HTML at a bad point) $ang_pos = strpos($INBETWEEN_TEXT, '<'); if ($ang_pos !== false) { $INBETWEEN_TEXT = substr($INBETWEEN_TEXT, 0, $ang_pos); } } $new .= fix_entities($INBETWEEN_TEXT); } // Check we have everything closed while (count($TAG_STACK) != 0) { $previous = array_pop($TAG_STACK); $new .= '</' . $previous . '>'; } // Remove some empty tags that shouldn't be empty (e.g. table) $may_not_be_empty = array('br', 'hr', 'table', 'tbody', 'tfoot', 'thead', 'tr', 'dd', 'dt', 'dl', 'li', 'ol', 'ul', 'rbc', 'rtc', 'rb', 'rt', 'rp', 'abbr', 'acronym', 'cite', 'dfn', 'ruby', 'bdo', 'img', 'param', 'input', 'select', 'object', 'caption', 'label', 'base', 'body', 'col', 'colgroup', 'map', 'optgroup', 'option', 'legend', 'area', 'form'); foreach ($may_not_be_empty as $t) { $new = preg_replace('#<' . $t . '(\\s[^>]*)?' . '>\\s*</' . $t . '>#', '', $new); } unset($OUT); unset($TAG_STACK); if ($is_escaped) { ocp_mark_as_escaped($new); } return $new; }
/** * Output an XML-RPC packet (hopefully) to the AJAX in the frontend. * * @return boolean Success? */ function output_xml() { if (count($this->parsed_input) < 1) { return false; } header('Content-Type: text/xml'); header('HTTP/1.0 200 Ok'); if (is_object($this->output[STREAM_STDCOMMAND])) { $this->output[STREAM_STDCOMMAND] = $this->output[STREAM_STDCOMMAND]->evaluate(); } if (is_object($this->output[STREAM_STDHTML])) { $this->output[STREAM_STDHTML] = $this->output[STREAM_STDHTML]->evaluate(); } if (is_object($this->output[STREAM_STDOUT])) { $this->output[STREAM_STDOUT] = $this->output[STREAM_STDOUT]->evaluate(); } if (is_object($this->output[STREAM_STDERR])) { $this->output[STREAM_STDERR] = $this->output[STREAM_STDERR]->evaluate(); } $output = '<' . '?xml version="1.0" encoding="utf-8" ?' . '> <response> <result> <command>' . xmlentities($this->current_input) . '</command> <stdcommand>' . $this->output[STREAM_STDCOMMAND] . '</stdcommand> <stdhtml><div xmlns="http://www.w3.org/1999/xhtml">' . $this->output[STREAM_STDHTML] . '</div></stdhtml> <stdout>' . xmlentities($this->output[STREAM_STDOUT]) . '</stdout> <stderr>' . xmlentities($this->output[STREAM_STDERR]) . '</stderr> <stdnotifications>' . get_queued_messages() . '</stdnotifications> </result> </response>'; if ($GLOBALS['XSS_DETECT']) { if (ocp_is_escaped($this->output[STREAM_STDHTML])) { ocp_mark_as_escaped($output); } } echo $output; set_value('last_occle_command', strval(time())); return true; }
/** * Get the human-readable form of a language id, or a language entry from a language INI file. * * @param ID_TEXT The language id * @param ?mixed The first token [string or tempcode] (replaces {1}) (NULL: none) * @param ?mixed The second token [string or tempcode] (replaces {2}) (NULL: none) * @param ?mixed The third token (replaces {3}). May be an array of [of string], to allow any number of additional args (NULL: none) * @param ?LANGUAGE_NAME The language to use (NULL: users language) * @param boolean Whether to cause ocPortal to exit if the lookup does not succeed * @return ?mixed The human-readable content (NULL: not found). String normally. Tempcode if tempcode parameters. */ function _do_lang($codename, $token1 = NULL, $token2 = NULL, $token3 = NULL, $lang = NULL, $require_result = true) { global $LANGUAGE, $USER_LANG_CACHED, $RECORD_LANG_STRINGS, $XSS_DETECT, $PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED; if ($lang === NULL) { $lang = $USER_LANG_CACHED === NULL ? user_lang() : $USER_LANG_CACHED; } if ($GLOBALS['SEMI_DEBUG_MODE']) { $pos = strpos($codename, '='); if ($pos !== false) { // Find loaded file with smallest levenstein distance to current page $best = mixed(); $best_for = NULL; global $LANGS_REQUESTED; foreach (array_keys($LANGS_REQUESTED) as $possible) { $dist = levenshtein(get_page_name(), $possible); if (is_null($best) || $best > $dist) { $best = $dist; $best_for = $possible; } } $save_path = get_file_base() . '/lang/' . fallback_lang() . '/' . $best_for . '.ini'; if (!is_file($save_path)) { $save_path = get_file_base() . '/lang_custom/' . fallback_lang() . '/' . $best_for . '.ini'; } // Tack language strings onto this file list($codename, $value) = explode('=', $codename, 2); $myfile = fopen($save_path, 'at'); fwrite($myfile, "\n" . $codename . '=' . $value); fclose($myfile); // Fake-load the string $LANGUAGE[$lang][$codename] = $value; // Go through all required files, doing a string replace if needed $included_files = get_included_files(); foreach ($included_files as $inc) { $orig_contents = file_get_contents($inc); $contents = str_replace("'" . $codename . '=' . $value . "'", "'" . $codename . "'", $orig_contents); if ($orig_contents != $contents) { $myfile = fopen($inc, 'wt'); fwrite($myfile, $contents); fclose($myfile); } } } } $there = isset($LANGUAGE[$lang][$codename]); if (!$there) { $pos = strpos($codename, ':'); if ($pos !== false) { require_lang(substr($codename, 0, $pos), NULL, NULL, !$require_result); $codename = substr($codename, $pos + 1); } $there = isset($LANGUAGE[$lang][$codename]); } if ($RECORD_LANG_STRINGS) { global $RECORDED_LANG_STRINGS; $RECORDED_LANG_STRINGS[$codename] = 1; } if (!$there && (!isset($LANGUAGE[$lang]) || !array_key_exists($codename, $LANGUAGE[$lang]))) { global $PAGE_CACHE_LAZY_LOAD, $PAGE_CACHE_LANGS_REQUESTED, $LANG_REQUESTED_LANG; if ($PAGE_CACHE_LAZY_LOAD) { $PAGE_CACHE_LAZY_LOAD = false; // We can't be lazy any more, but we will keep growing our pool so hopefully CAN be lazy the next time foreach ($PAGE_CACHE_LANGS_REQUESTED as $request) { list($that_codename, $that_lang) = $request; unset($LANG_REQUESTED_LANG[$that_lang][$that_codename]); require_lang($that_codename, $that_lang, NULL, true); } $ret = _do_lang($codename, $token1, $token2, $token3, $lang, $require_result); if ($ret === NULL) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = NULL; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } return $ret; } require_all_open_lang_files($lang); } if ($lang == 'xxx') { return 'xxx'; } // Helpful for testing language compliancy. We don't expect to see non x's if we're running this language if (!isset($LANGUAGE[$lang][$codename]) && ($require_result || !isset($LANGUAGE[$lang]) || !array_key_exists($codename, $LANGUAGE[$lang]))) { if ($lang != fallback_lang()) { $ret = do_lang($codename, $token1, $token2, $token3, fallback_lang(), $require_result); if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename]) && isset($PAGE_CACHE_LANG_LOADED[fallback_lang()][$codename])) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $PAGE_CACHE_LANG_LOADED[fallback_lang()][$codename]; // Will have been cached into fallback_lang() from the nested do_lang call, we need to copy it into our cache bucket for this language if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } } return $ret; } else { if ($require_result) { global $USER_LANG_LOOP, $REQUIRE_LANG_LOOP; //print_r(debug_backtrace()); if ($USER_LANG_LOOP == 1) { critical_error('RELAY', 'Missing language code: ' . escape_html($codename) . '. This language code is required to produce error messages, and thus a critical error was prompted by the non-ability to show less-critical error messages. It is likely the source language files (lang/' . fallback_lang() . '/*.ini) for ocPortal on this website have been corrupted.'); } if ($REQUIRE_LANG_LOOP >= 2) { return ''; } // Probably failing to load global.ini, so just output with some text missing require_code('view_modes'); erase_cached_language(); require_code('site'); attach_message(do_lang_tempcode('MISSING_LANG_ENTRY', escape_html($codename)), 'warn'); return ''; } else { return NULL; } } } if ($PAGE_CACHE_FILE !== NULL) { if (!isset($PAGE_CACHE_LANG_LOADED[$lang][$codename]) && (!isset($PAGE_CACHE_LANG_LOADED[$lang]) || !array_key_exists($codename, $PAGE_CACHE_LANG_LOADED[$lang]))) { $PAGE_CACHE_LANG_LOADED[$lang][$codename] = $LANGUAGE[$lang][$codename]; if ($GLOBALS['MEM_CACHE'] !== NULL) { persistant_cache_set($PAGE_CACHE_FILE, $PAGE_CACHE_LANG_LOADED); } else { open_page_cache_file(); @rewind($PAGE_CACHE_FILE); @flock($PAGE_CACHE_FILE, LOCK_EX); @ftruncate($PAGE_CACHE_FILE, 0); @fwrite($PAGE_CACHE_FILE, serialize($PAGE_CACHE_LANG_LOADED)); @flock($PAGE_CACHE_FILE, LOCK_UN); } } } // Put in parameters static $non_plural_non_vowel = array('1', 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z', '{'); $looked_up = $LANGUAGE[$lang][$codename]; if ($looked_up === NULL) { return NULL; // Learning cache pool has told us this string definitely does not exist } $out = str_replace('\\n', "\n", $looked_up); $plural_or_vowel_check = strpos($out, '|') !== false; if ($XSS_DETECT) { ocp_mark_as_escaped($out); } if ($token1 !== NULL) { if (is_object($token1) && $token2 === NULL || $token2 !== NULL && is_object($token2)) { $bits = preg_split('#\\{\\d[^\\}]*\\}#', $out, 2, PREG_SPLIT_OFFSET_CAPTURE); $ret = new ocp_tempcode(); foreach ($bits as $bit) { if ($XSS_DETECT) { ocp_mark_as_escaped($bit[0]); } $at = $bit[1]; if ($at != 0) { if ($out[$at - 2] == '1') { $ret->attach($token1); } elseif ($out[$at - 2] == '2') { $ret->attach($token2); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '1|') { $exploded = explode('|', $out[$at - 2]); $_token = $token1->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } elseif ($plural_or_vowel_check && substr($out[$at - 2], 0, 2) == '2|') { $exploded = explode('|', $out[$at - 2]); $_token = $token2->evaluate(); $_token_denum = str_replace(',', '', $_token); $ret->attach(in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($_token, 0, 1)), $non_plural_non_vowel) ? $exploded[1] : $exploded[2]); } } $ret->attach($bit[0]); } return $ret; } elseif ($token1 !== NULL) { $out = str_replace('{1}', $token1, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{1\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token1, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token1)) { ocp_mark_as_escaped($out); } } if ($token2 !== NULL) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{2}', $token2, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token1); $out = preg_replace('#\\{2\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token2, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token2) && $escaped) { ocp_mark_as_escaped($out); } if ($token3 !== NULL) { $i = 3; if (!is_array($token3)) { $token3 = array($token3); } foreach ($token3 as $token) { if ($XSS_DETECT) { $escaped = ocp_is_escaped($out); } $out = str_replace('{' . strval($i) . '}', $token, $out); if ($plural_or_vowel_check) { $_token_denum = str_replace(',', '', $token); $out = preg_replace('#\\{' . strval($i) . '\\|(.*)\\|(.*)\\}#U', in_array(is_numeric($_token_denum) ? $_token_denum : ocp_mb_strtolower(ocp_mb_substr($token, 0, 1)), $non_plural_non_vowel) ? '\\1' : '\\2', $out); } if ($XSS_DETECT && ocp_is_escaped($token) && $escaped) { ocp_mark_as_escaped($out); } $i++; } } } } return $out; }
/** * Handle truncation symbols in all their complexity * * @param array Parameters passed to the symbol (0=text, 1=amount, 2=tooltip?, 3=is_html?, 4=use as grammatical length rather than HTML byte length, 5=fractional-deviation-tolerance for grammar-preservation) * @param string The type of truncation to do * @set left right spread * @param ?mixed Tooltip to add on, but only if we end up creating our own tooltip (NULL: none) * @return string The result. */ function symbol_truncator($param, $type, $tooltip_if_truncated = NULL) { $value = ''; if (is_object($param[0])) { $param[0] = $param[0]->evaluate(); if (!isset($param[2])) { $param[2] = '0'; } $param[3] = '1'; } if ($GLOBALS['XSS_DETECT']) { $is_escaped = ocp_is_escaped($param[0]); } $amount = intval(isset($param[1]) ? $param[1] : '60'); $is_html = isset($param[3]) && $param[3] == '1'; if ($is_html) { $not_html = @html_entity_decode(strip_tags($param[0]), ENT_QUOTES, get_charset()); // In case it contains HTML. This is imperfect, but having to cut something up is imperfect from the offset. $html = $param[0]; if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($html); } if ($html == $not_html && strpos($html, '&') === false && strpos($html, '<') === false) { $is_html = false; } // Conserve memory } else { $not_html = $param[0]; $html = escape_html($param[0]); } if (ocp_mb_strlen($not_html) > $amount) { $tooltip = isset($param[2]) && $param[2] == '1'; $literal_pos = isset($param[4]) ? $param[4] == '1' : false; $grammar_completeness_tolerance = isset($param[5]) ? floatval($param[5]) : 0.0; if ($is_html || $grammar_completeness_tolerance != 0.0) { require_code('xhtml'); } $truncated = $not_html; switch ($type) { case 'left': $temp = $is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $amount - 3, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $amount - 3)); if ($temp != $html && in_array(substr($temp, -1), array('.', '?', '!'))) { $temp .= '<br />'; } // so the "..." does not go right after the sentence terminator $truncated = $temp == $html ? $temp : str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), rtrim($temp) . '…'); break; case 'expand': $temp = $is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $amount - 3, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $amount - 3)); if ($temp != $html && in_array(substr($temp, -1), array('.', '?', '!'))) { $temp .= '<br />'; } // so the "..." does not go right after the sentence terminator $_truncated = do_template('COMCODE_HIDE', array('TEXT' => protect_from_escaping($temp), 'CONTENT' => protect_from_escaping($html))); $truncated = $_truncated->evaluate(); break; case 'right': $truncated = str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), '…' . ltrim($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, -$amount - 3, NULL, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, -$amount - 3)))); break; case 'spread': $pos = intval(floor(floatval($amount) / 2.0)) - 1; $truncated = str_replace(array('</p>…', '</div>…'), array('…</p>', '…</div>'), rtrim(($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, 0, $pos, $literal_pos, false, $grammar_completeness_tolerance) : escape_html(ocp_mb_substr($not_html, 0, $pos))) . '…' . ltrim($is_html || $grammar_completeness_tolerance != 0.0 ? xhtml_substr($html, -$pos - 1) : escape_html(ocp_mb_substr($not_html, -$pos - 1))))); break; } if ($tooltip) { if (!is_null($tooltip_if_truncated)) { $tif = is_object($tooltip_if_truncated) ? $tooltip_if_truncated->evaluate() : $tooltip_if_truncated; if (strpos($tif, $html) !== false) { $html = $tif; } else { $html .= ' – ' . $tif; } } $tpl = strpos($truncated, '<div') !== false || strpos($truncated, '<p') !== false || strpos($truncated, '<table') !== false ? 'CROP_TEXT_MOUSE_OVER' : 'CROP_TEXT_MOUSE_OVER_INLINE'; $value_tempcode = do_template($tpl, array('_GUID' => '36ae945ed864633cfa0d67e5c3f2d1c8', 'TEXT_SMALL' => $truncated, 'TEXT_LARGE' => $html)); $value = $value_tempcode->evaluate(); if ($GLOBALS['XSS_DETECT']) { ocp_mark_as_escaped($value); } } else { $value = $truncated; } } else { $value = $html; } if ($GLOBALS['XSS_DETECT']) { if ($is_escaped || !$is_html) { ocp_mark_as_escaped($value); } } return $value; }