<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  Christopher Graham
 * @package    core
 */

/**
 * Standard code module initialisation function.
 *
 * @ignore
 */
function init__symbols()
{
    global $BLOCKS_CACHE, $PAGES_CACHE, $PANELS_CACHE, $STATIC_TEMPLATE_TEST_MODE, $SYMBOLS2_CAUSE;
    $BLOCKS_CACHE = [];
    $PAGES_CACHE = [];
    $PANELS_CACHE = [];
    $STATIC_TEMPLATE_TEST_MODE = false;
    $SYMBOLS2_CAUSE = [];
}

/**
 * Evaluate a conventional Tempcode construct, handling escaping.
 *
 * @param  string $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  integer $type The type of symbol this is (TC_SYMBOL, TC_LANGUAGE_REFERENCE)
 * @set 0 2
 * @param  ID_TEXT $name The name of the symbol
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @param  ID_TEXT $template_name The name of the template this came from (blank: not from a file / unknown); only passed for directives to avoid infinite loops with INCLUDE
 * @return mixed The result. Either Tempcode, or a string.
 */
function ecv(string $lang, array $escaped, int $type, string $name, array $param, string $template_name = '')
{
    $name = trim($name);

    // SYMBOLS...
    if ($type === TC_SYMBOL) {
        // Built-in
        if ($name === '?') {
            $value = call_user_func('ecv_TERNARY', $lang, $escaped, $param);
        } elseif (function_exists('ecv_' . $name)) {
            $value = call_user_func('ecv_' . $name, $lang, $escaped, $param);
        } else {
            // Maybe a hook?
            static $extra_symbols = null;
            if ($extra_symbols === null) {
                if (running_script('install')) {
                    $extra_symbols = ['BETA_CSS_PROPERTY' => []]; // Needed for installer to look good ('find_all_hooks' won't run in initial steps of quick installer)
                } else {
                    $extra_symbols = [];
                    $hooks = find_all_hooks('systems', 'symbols');
                    foreach (array_keys($hooks) as $hook) {
                        $extra_symbols[$hook] = [];
                    }
                }
            }
            if (isset($extra_symbols[$name])) {
                if (!isset($extra_symbols[$name]['ob'])) {
                    require_code('hooks/systems/symbols/' . filter_naughty_harsh($name));
                    $extra_symbols[$name]['ob'] = object_factory('Hook_symbol_' . filter_naughty_harsh($name));
                }
                $value = $extra_symbols[$name]['ob']->run($param);

                if (!empty($escaped)) {
                    if (is_object($value)) {
                        $value = $value->evaluate();
                    }
                    apply_tempcode_escaping($escaped, $value);
                }
            } else {
                // A less common symbol?
                if (defined($name)) {
                    $value = @strval(constant($name));
                    if (!is_string($value)) {
                        $value = strval($value);
                    }

                    if (!empty($escaped)) {
                        if (is_object($value)) {
                            $value = $value->evaluate();
                        }
                        apply_tempcode_escaping($escaped, $value);
                    }
                } else {
                    require_code('symbols2');
                    if (function_exists('ecv2_' . $name)) {
                        global $SYMBOLS2_CAUSE;
                        $SYMBOLS2_CAUSE[] = $name;

                        $value = call_user_func('ecv2_' . $name, $lang, $escaped, $param); // A constant?
                    } else { // Error :-(
                        $value = '';
                        if ($GLOBALS['XSS_DETECT']) {
                            ocp_mark_as_escaped($value);
                        }
                        if (!running_script('install')) {
                            trigger_error(do_lang('MISSING_SYMBOL', escape_html($name)), E_USER_NOTICE);

                            persistent_cache_delete('HOOKS'); // May be a cache issue, find it on refresh
                        }
                    }
                }
            }
        }

        return $value;
    }

    // DIRECTIVES...

    if ($type === TC_DIRECTIVE) {
        $value = '';
        if ($GLOBALS['XSS_DETECT']) {
            ocp_mark_as_escaped($value);
        }

        // In our param we should have a map of bubbled template parameters (under 'vars') and our numbered directive parameters

        if ($param === null) {
            $param = [];
        }

        // Closure-based Tempcode parser may send in strings, so we need to adapt...
        static $directive_tempcodified_params = [];
        foreach ($param as $key => $val) {
            if (is_string($val)) {
                if (!isset($directive_tempcodified_params[$val])) {
                    $directive_tempcodified_params[$val] = make_string_tempcode($val);
                }
                $param[$key] = $directive_tempcodified_params[$val];
            }
        }

        if (!isset($param['vars'])) {
            $param['vars'] = [];
        }

        switch ($name) {
            case 'IF':
                ecv_IF($value, $lang, $escaped, $param);
                break;

            case 'IF_EMPTY':
                ecv_IF_EMPTY($value, $lang, $escaped, $param);
                break;

            case 'IF_NON_EMPTY':
                ecv_IF_NON_EMPTY($value, $lang, $escaped, $param);
                break;

            case 'IF_PASSED':
                ecv_IF_PASSED($value, $lang, $escaped, $param);
                break;

            case 'IF_NON_PASSED':
                ecv_IF_NON_PASSED($value, $lang, $escaped, $param);
                break;

            case 'IF_PASSED_AND_TRUE':
                ecv_IF_PASSED_AND_TRUE($value, $lang, $escaped, $param);
                break;

            case 'IF_NON_PASSED_OR_FALSE':
                ecv_IF_NON_PASSED_OR_FALSE($value, $lang, $escaped, $param);
                break;

            case 'WHILE':
                require_code('symbols2');
                ecv2_WHILE($value, $lang, $escaped, $param);
                break;

            case 'LOOP':
                ecv_LOOP($value, $lang, $escaped, $param);
                break;

            case 'PARAM_INFO':
                $_value = do_template('PARAM_INFO', ['_GUID' => '960e11880fbc512a3170756ca43384fd', 'MAP' => $param['vars']]);
                $value = $_value->evaluate();
                break;

            case 'TRIM':
                $value = trim($param[0]->evaluate());
                break;

            case 'FRACTIONAL_EDITABLE':
                require_javascript('fractional_edit');

                foreach (array_keys($param) as $key) {
                    if (!is_numeric($key)) {
                        unset($param[$key]);
                    }
                }
                if (isset($param[3])) {
                    $edit_text = $param[0]->evaluate();
                    $edit_param_name = $param[1]->evaluate();
                    $edit_page_link = $param[2]->evaluate();
                    $supports_comcode = (isset($param[4]) ? $param[3]->evaluate() : '0') === '1';
                    $explicit_editing_links = (isset($param[5]) ? $param[4]->evaluate() : '0') === '1';
                    $has_permission = (isset($param[6]) ? ($param[5]->evaluate() === '1') : null);

                    list($zone, $attributes,) = page_link_decode($edit_page_link);
                    if ($zone === '_SEARCH') {
                        $zone = get_module_zone($attributes['page']);
                    }
                    if (
                        (get_option('fractional_editing') == '1') &&
                        (has_actual_page_access(get_member(), $attributes['page'], $zone)) &&
                        (($has_permission === true) || (($has_permission === null) && (has_zone_access(get_member(), 'adminzone'))))
                    ) {
                        $keep = symbol_tempcode('KEEP');
                        $url = find_script('fractional_edit') . '?edit_param_name=' . urlencode($edit_param_name) . '&supports_comcode=' . ($supports_comcode ? '1' : '0') . '&zone=' . urlencode($zone) . $keep->evaluate();
                        if (!empty($attributes)) {
                            $url .= '&' . http_build_query($attributes);
                        }

                        $_value = $param[count($param) - 1];
                        $_value = do_template('FRACTIONAL_EDIT', [
                            '_GUID' => '075ac126c427d28b309004bc67b32b08',
                            'VALUE' => $_value,
                            'URL' => $url,
                            'EDIT_TEXT' => $edit_text,
                            'EDIT_PARAM_NAME' => $edit_param_name,
                            'EXPLICIT_EDITING_LINKS' => $explicit_editing_links,
                        ]);
                        $value = $_value->evaluate();
                    } else {
                        $value = $param[count($param) - 1]->evaluate();
                    }
                }
                break;

            case 'SET':
                if (isset($param[1])) {
                    unset($param['vars']);
                    global $TEMPCODE_SETGET;
                    if (isset($param[1])) {
                        $TEMPCODE_SETGET[isset($param[0]->codename/*faster than is_object*/) ? $param[0]->evaluate() : $param[0]] = $param[1];
                    } else {
                        $param_copy = [];
                        foreach ($param as $i => $x) {
                            if ($i !== 0) {
                                $param_copy[] = isset($x->codename/*faster than is_object*/) ? $x->evaluate() : $x;
                            }
                        }
                        $TEMPCODE_SETGET[isset($param[0]->codename/*faster than is_object*/) ? $param[0]->evaluate() : $param[0]] = implode(',', $param_copy);
                    }
                }
                break;

            case 'IMPLODE':
                if (isset($param[1])) {
                    $key = $param[1]->evaluate();
                    $array = array_key_exists($key, $param['vars']) ? $param['vars'][$key] : [];
                    if ((isset($param[2])) && ($param[2]->evaluate() == '1')) {
                        $delim = $param[0]->evaluate();
                        foreach ($array as $key => $val) {
                            if ($value != '') {
                                $value .= $delim;
                            }
                            $value .= escape_html((is_integer($key) ? integer_format($key) : $key) . '=' . $val);
                        }
                    } else {
                        foreach ($array as &$key) {
                            if ((isset($param[3])) && ($param[3]->evaluate() == '1')) {
                                $key = escape_html($key);
                            }
                        }
                        $value = implode($param[0]->evaluate(), $array);
                    }
                }
                break;

            case 'COUNT':
                if (isset($param[0])) {
                    $key = $param[0]->evaluate();
                    $array = array_key_exists($key, $param['vars']) ? $param['vars'][$key] : [];
                    if (is_array($array)) {
                        $value = strval(count($array));
                    }
                }
                break;

            case 'BOX':
                unset($param['vars']);
                $title = isset($param[1]) ? $param[0]->evaluate() : '';
                $box_type = isset($param[2]) ? $param[1]->evaluate() : '';
                $width = isset($param[3]) ? $param[2]->evaluate() : '';
                $options = isset($param[4]) ? $param[3]->evaluate() : '';
                $meta = isset($param[5]) ? $param[4]->evaluate() : '';
                $links = isset($param[6]) ? $param[5]->evaluate() : '';
                $top_links = isset($param[7]) ? $param[6]->evaluate() : '';
                $class = isset($param[8]) ? $param[7]->evaluate() : '';
                $tmp = put_in_standard_box(array_pop($param), make_string_tempcode($title), $box_type, $width, $options, $meta, $links, $top_links, $class);
                $value = $tmp->evaluate();
                break;

            case 'NO_PREPROCESSING':
                $value = $param[0]->evaluate();
                break;

            case 'OF':
                if (isset($param[1])) {
                    $key = $param[0]->evaluate();
                    $x = $param[1]->evaluate();

                    $array = isset($param['vars'][$key]) ? $param['vars'][$key] : [];
                    if (is_array($array)) {
                        $x2 = is_numeric($x) ? intval($x) : $x;
                        if (is_integer($x2)) {
                            if ($x2 < 0) {
                                $x2 = count($array) - 1;
                            }
                        }
                        $value = isset($array[$x2]) ? $array[$x2] : '';
                        if (is_object($value)) {
                            $value = $value->evaluate();
                        }
                    }
                }
                break;

            case 'RECONTEXTUALISE_IDS':
                require_code('symbols2');
                ecv2_RECONTEXTUALISE_IDS($value, $lang, $escaped, $param);
                break;

            case 'INCLUDE':
                if (isset($param[1])) {
                    $tpl_params = $param['vars'];
                    $var_data = $param[count($param) - 2]->evaluate();
                    $substitutions = parse_tempcode_include($var_data, $tpl_params);
                    $_template_name = $param[0]->evaluate();
                    $ex = isset($param[2]) ? $param[1]->evaluate() : '';
                    if ($ex == '') {
                        $ex = '.tpl';
                    }
                    $td = isset($param[3]) ? $param[2]->evaluate() : '';
                    if ($td == '') {
                        $td = 'templates';
                    }
                    $theme = isset($param[4]) ? $param[3]->evaluate() : '';
                    if ($theme == '') {
                        $theme = null;
                    }
                    $force_original = isset($param[5]) ? $param[4]->evaluate() : '';
                    if ($force_original != '1') {
                        $force_original = '0';
                    }

                    $_value = do_template($_template_name, $tpl_params, null, true, null, $ex, $td, $theme, ($template_name == $_template_name) || ($force_original == '1'));
                    $value = $_value->evaluate();

                    apply_tempcode_substitutions($value, $substitutions);
                }
                break;

            case 'IF_IN_ARRAY':
                if (isset($param[2])) {
                    $key = $param[0]->evaluate();
                    $array = array_key_exists($key, $param['vars']) ? $param['vars'][$key] : [];
                    $value = '';
                    if ($GLOBALS['XSS_DETECT']) {
                        ocp_mark_as_escaped($value);
                    }
                    $i = 1;
                    while (isset($param[$i + 1])) {
                        $checking_in = $param[$i]->evaluate();
                        if (in_array($checking_in, $array)) {
                            $value = $param[count($param) - 2]->evaluate();
                            break;
                        }
                        $i++;
                    }
                }
                break;

            case 'IF_NOT_IN_ARRAY':
                if (isset($param[2])) {
                    $key = $param[0]->evaluate();
                    $array = array_key_exists($key, $param['vars']) ? $param['vars'][$key] : [];
                    $value = '';
                    if ($GLOBALS['XSS_DETECT']) {
                        ocp_mark_as_escaped($value);
                    }
                    $ok = true;
                    $i = 1;
                    while (isset($param[$i + 1])) {
                        $checking_in = $param[$i]->evaluate();
                        if (in_array($checking_in, $array)) {
                            $ok = false;
                            break;
                        }
                        $i++;
                    }
                    if ($ok) {
                        $value = $param[$i]->evaluate();
                    }
                }
                break;

            case 'IF_ARRAY_EMPTY':
                if (isset($param[0])) {
                    $looking_at = $param[0]->evaluate();
                    if (array_key_exists($looking_at, $param['vars'])) {
                        if ((is_array($param['vars'][$looking_at])) && (empty($param['vars'][$looking_at]))) {
                            $value = $param[1]->evaluate();
                        }
                    }
                }
                break;

            case 'IF_ARRAY_NON_EMPTY':
                if (isset($param[0])) {
                    $looking_at = $param[0]->evaluate();
                    if (array_key_exists($looking_at, $param['vars'])) {
                        if ((is_array($param['vars'][$looking_at])) && (!empty($param['vars'][$looking_at]))) {
                            $value = $param[1]->evaluate();
                        }
                    }
                }
                break;

            case 'COMMENT':
                break;

            case 'CASES':
                if (isset($param[1])) {
                    $value = '';
                    $compare = $param[0]->evaluate();
                    $substring = (isset($param[2]) && $param[1]->evaluate() == '1');
                    $regexp = (isset($param[2]) && $param[1]->evaluate() == '2');
                    $explode = explode(chr(10), trim($param[isset($param[2]) ? 2 : 1]->evaluate()));
                    foreach ($explode as $i => $case) {
                        if (strpos($case, '=') === false) {
                            continue;
                        }
                        list($compare_case, $value_case) = explode('=', $case, 2);
                        $compare_case = trim($compare_case);
                        if (
                            ((!$substring) && (!$regexp) && ($compare_case == $compare)) || // Exact match
                            (($substring) && (($compare_case == '') || (strpos($compare, $compare_case) !== false))) || // Substring
                            (($regexp) && (preg_match('#' . str_replace('#', '\#', $compare_case) . '#', $compare) != 0)) || // Regexp
                            (($compare_case == '') && (!isset($explode[$i + 1]))) // The final default case
                        ) {
                            $value = $value_case;
                            break;
                        }
                    }
                }
                break;

            case 'CSS_MODE':
                if (isset($param[1])) {
                    $param[0] = $param[0]->evaluate();
                    $param[1] = $param[1]->evaluate();

                    global $TEMPCODE_SETGET;
                    if (!isset($TEMPCODE_SETGET[$param[0]])) {
                        attach_message(do_lang_tempcode('UNKNOWN_CSS_MODE', escape_html($param[0])));
                        break;
                    }

                    $css_mode = explode(',', $TEMPCODE_SETGET[$param[0]]);
                    if (!isset($css_mode[1]) || !in_array($css_mode[1], ['mobileModeDiscard', 'mobileModeKeep', 'mobileModeMedia'])) {
                        $css_mode[1] = 'mobileModeDiscard';
                    }

                    $matches = [];
                    if (preg_match('#^(\d+)-(\d+|infinity)$#', $css_mode[0], $matches) == 0) {
                        attach_message(do_lang_tempcode('CORRUPT_CSS_MODE', escape_html($param[0])));
                        break;
                    }

                    if ((is_mobile()) && ($css_mode[1] == 'mobileModeKeep')) {
                        // Optimisation
                        $value = $param[1];
                    } elseif ((!is_mobile()) || ($css_mode[1] != 'mobileModeDiscard')) {
                        // Media rules
                        $from = $matches[1];
                        $to = $matches[2];
                        $media_rule = '';
                        if ($from != '0') {
                            $media_rule .= '(min-width: ' . $from . 'px)';
                        }
                        if ($to != 'infinity') {
                            if ($media_rule != '') {
                                $media_rule .= ' and ';
                            }
                            $media_rule .= '(max-width: ' . $to . 'px)';
                        }
                        $value = '@media ' . $media_rule . ' { ' . $param[1] . '}';
                    }
                } else {
                    attach_message(do_lang_tempcode('CORRUPT_CSS_MODE', escape_html($param[0])));
                }

                break;

            case 'MAKE_MOBILE':
                $value = $param[count($param) - 2]->evaluate();

                if (is_mobile()) {
                    // Easy table conversion for mobile
                    $table_cleanup_func = function ($matches) {
                        static $is_in_remaining_table = false;

                        if ($matches[1] == '') { // Opening element
                            $element = cms_strtolower_ascii($matches[2]);
                            $attributes = isset($matches[3]) ? $matches[3] : '';

                            if ($element == 'table') {
                                if (strpos($attributes, 'responsive-table') !== false) {
                                    $is_in_remaining_table = true;
                                    return $matches[0];
                                }

                                // Real table with borders --> responsive table
                                if ((preg_match('#\s+border=["\']?[1-9]#i', $attributes) != 0) || (preg_match('#\s+style="[^"]*border:\s*[1-9]#i', $attributes) != 0) || (strpos($attributes, 'results-table') !== false)) {
                                    $is_in_remaining_table = true;
                                    if (preg_match('#\s+class=["\']#i', $attributes) == 0) {
                                        $attributes .= ' class="responsive-table forceful"';
                                    } else {
                                        $attributes = preg_replace('#\s+class=["\']#i', '$0responsive-table forceful ', $attributes);
                                    }
                                    return '<table' . $attributes . '>';
                                }

                                // Converting to divs...

                                $is_in_remaining_table = false;
                            }

                            if ($is_in_remaining_table) {
                                return $matches[0];
                            }

                            $attributes = preg_replace('#\s+(cellspacing|cellpadding|border|width|height|align)="[^"]*"#', '', $attributes);
                            return '<div' . $attributes . '>';
                        }

                        // Closing element

                        if ($is_in_remaining_table) {
                            return $matches[0];
                        }

                        return '</div>';
                    };
                    $value = preg_replace_callback('#<()(table|tbody|thead|tr|th|td)(\s[^<>]*)?' . '>#i', $table_cleanup_func, $value);
                    $value = preg_replace_callback('#<(/)(table|tbody|thead|tr|th|td)>#', $table_cleanup_func, $value);
                }

                // Easy responsive images
                if ((isset($param[1])) && (is_numeric($param[0]->evaluate()))) {
                    $viewport_switch = intval($param[0]->evaluate());
                } else {
                    $viewport_switch = 983;
                }
                $new_value = '';
                $matches = [];
                $last_offset = 0;
                $num_matches = preg_match_all('#(<img\s[^<>]*src=")([^"]*)(")([^<>]*>)#i', $value, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
                for ($i = 0; $i < $num_matches; $i++) {
                    $this_offset = $matches[$i][0][1];

                    $img_html = $matches[$i][0][0];
                    if (($GLOBALS['XSS_DETECT']) && (ocp_is_escaped($value))) {
                        ocp_mark_as_escaped($img_html);
                    }

                    $substr = substr($value, $last_offset, $this_offset - $last_offset);
                    if (($GLOBALS['XSS_DETECT']) && (ocp_is_escaped($value))) {
                        ocp_mark_as_escaped($substr);
                    }
                    $new_value .= $substr;

                    if (strpos($img_html, 'srcset=') !== false || strpos($img_html, 'sizes=') !== false || substr($value, $this_offset - 14, 14) == '<!--picture-->') {
                        // Already responsive?
                        $new_value .= $img_html;
                        $last_offset = $this_offset + strlen($img_html);
                        continue;
                    }

                    $mobile_url = null;

                    // Explicitly defined alternate image?
                    $matches2 = [];
                    if (preg_match('#\sdata-src-mobile="([^"]*)"#', $img_html, $matches2) != 0) {
                        $mobile_url = $matches2[1];
                    }

                    // Find alternate images
                    $url = html_entity_decode($matches[$i][2][0], ENT_QUOTES);
                    if ($mobile_url === null) {
                        $url_stub = get_custom_base_url() . '/uploads/filedump/';
                        if (substr($url, 0, strlen($url_stub)) == $url_stub) {
                            $file_path = get_custom_file_base() . substr($url, strlen(get_custom_base_url()));
                            if (is_file($file_path)) {
                                $ext = get_file_extension($file_path);
                                $extensions = array_unique(array_merge([$ext], ['png', 'gif', 'jpg', 'jpeg', 'webp']));
                                foreach ($extensions as $_ext) {
                                    $mobile_file_path = dirname($file_path) . '/' . basename($file_path, '.' . $ext) . '_mobile.' . $_ext;
                                    if (!is_file($mobile_file_path)) {
                                        $mobile_file_path = dirname($file_path) . '/' . basename($file_path, '.' . $ext) . '-mobile.' . $_ext;
                                    }
                                    if (is_file($mobile_file_path)) {
                                        $mobile_url = get_custom_base_url() . substr($mobile_file_path, strlen(get_custom_file_base()));
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if ($mobile_url === null) {
                        // No mobile version
                        $new_value .= $img_html;
                        $last_offset = $this_offset + strlen($img_html);
                        continue;
                    }

                    $img_start = $matches[$i][1][0];
                    $img_src_close = $matches[$i][3][0];
                    $img_end = $matches[$i][4][0];
                    if (($GLOBALS['XSS_DETECT']) && (ocp_is_escaped($value))) {
                        ocp_mark_as_escaped($img_start);
                        ocp_mark_as_escaped($img_src_close);
                        ocp_mark_as_escaped($img_end);
                    }

                    if (is_mobile()) {
                        // Simple <img> element with mobile version URL...

                        $new_value .= '<!--picture-->';
                        $new_value .= $img_start; // Front of img tag and start src attribute
                        $new_value .= escape_html($mobile_url); // Image URL
                        $new_value .= $img_src_close; // Close src attribute
                        $new_value .= $img_end; // End of img tag
                    } else {
                        if (get_zone_name() != 'cms') {
                            // Complex <picture> element...

                            $new_value .= '<picture>';

                            // Responsive design code for desktop
                            $new_value .= '<source media="(min-width: ' . strval($viewport_switch) . 'px)" srcset="' . escape_html($url) . '" />';

                            // Responsive design code for mobile
                            $new_value .= '<source media="(max-width: ' . strval($viewport_switch - 1) . 'px)" srcset="' . escape_html($mobile_url) . '" />';

                            $new_value .= $img_start; // Front of img tag and start src attribute
                            $new_value .= escape_html($url); // Non-responsive default image URL
                            $new_value .= $img_src_close; // Close src attribute
                            $new_value .= $img_end; // End of img tag

                            $new_value .= '</picture>';
                        } else {
                            // Simple <img> element with mobile version URL...

                            $new_value .= $img_start; // Front of img tag and start src attribute
                            $new_value .= escape_html($mobile_url); // Image URL
                            $new_value .= $img_src_close; // Close src attribute
                            $new_value .= $img_end; // End of img tag
                        }
                    }

                    $last_offset = $this_offset + strlen($img_html);
                }
                if ($num_matches > 0) {
                    $substr = substr($value, $last_offset);
                    if (($GLOBALS['XSS_DETECT']) && (ocp_is_escaped($value))) {
                        ocp_mark_as_escaped($substr);
                    }

                    $new_value .= $substr;
                    $new_value = preg_replace('#\sdata-src-mobile="([^"]*)"#', '', $new_value);
                    $value = $new_value;
                }

                if (is_mobile()) {
                    // Allow targeted CSS
                    $value = '<div class="make-mobile">' . $value . '</div>';
                }

                if (is_mobile()) {
                    // Remove centering
                    $value = preg_replace('#<(div|p)\s+style="text-align:\s*center;?">#i', '<$1>', $value);
                }

                if (is_mobile()) {
                    // Make details sections closed
                    $value = str_replace('<details open="open">', '<details>', $value);
                }

                if (!is_mobile()) {
                    if (get_zone_name() != 'cms') {
                        // Make some sections disappear
                        $value = preg_replace('#<details( class="[^"]*only-on-mobile[^"]*".*)</details>#Us', '<div$1</div>', $value);
                        $value = preg_replace('#<summary( class="[^"]*only-on-mobile[^"]*".*)</summary>#Us', '<div$1</div>', $value);
                    }
                }

                break;

            case 'HTML_COMPRESS':
                $options = isset($param[1]) ? explode('|', $param[0]) : [];
                $value = $param[count($param) - 2]->evaluate();
                if (get_param_integer('keep_minify', 1) == 1) {
                    if (in_array('comments', $options)) {
                        $value = preg_replace('#<!--.*-->#Us', '', $value);
                    }

                    if (in_array('delayjavascript', $options)) {
                        $insert_pos = stripos($value, '</body>');
                        if ($insert_pos !== false) {
                            $new_value = '';
                            $script_buildup = '';
                            $prior_pos_end = 0;
                            $end_len = strlen('</script>');
                            do {
                                $pos = stripos($value, '<script', $prior_pos_end);
                                if ($pos === false) {
                                    break; // Nothing more
                                }

                                $pos_end = stripos($value, '</script>', $pos);
                                if ($pos_end === false) {
                                    break; // Doesn't terminate, weird, nothing more
                                }
                                if ($pos_end > $insert_pos) {
                                    break; // Somehow this got past the end of the </body>
                                }

                                $before = substr($value, $prior_pos_end, $pos - $prior_pos_end);
                                $script = substr($value, $pos, $pos_end - $pos + $end_len);
                                if (strpos($script, ' data-optimize="no"') === false) {
                                    $new_value .= $before;
                                    $script_buildup .= "\n" . $script . "\n";
                                } else {
                                    $new_value .= $before . str_replace(' data-optimize="no"', '', $script);
                                }

                                $prior_pos_end = $pos_end + $end_len;
                            } while (true);
                            $after = substr($value, $prior_pos_end, $insert_pos - $prior_pos_end);
                            $tail = substr($value, $insert_pos);
                            $value = $new_value . $script_buildup . $tail;
                        }
                    }

                    $active_html_components = '<\w+(\s[^<>]*)?' . '>|</\w+>|/>|-->|<!DOCTYPE[^<>]*>|// <!\[CDATA\[|//\]\]>';
                    $value = preg_replace('#(' . $active_html_components . ')\s*\n\s*#i', "$1\n", $value);
                    $value = preg_replace('#\s*\n\s*(' . $active_html_components . ')#i', "\n$1", $value);

                    if ((in_array('relativeurls', $options)) || (in_array('protocolrelativeurls', $options))) {
                        $matches = [];
                        $is_outside_root = (preg_match('#^(.*/)(.*)$#', get_self_url_easy(), $matches) != 0);
                        if ($is_outside_root) {
                            $current_relative_base = $matches[1];
                            $current_relative_url = $matches[2];
                            $current_relative_base_len = strlen($current_relative_base);
                        }

                        for ($i = 0; $i < 2; $i++) { // 2 sweeps, because some tags may have multiple URLs in them
                            $matches = [];
                            $value_new = '';
                            $last_offset = 0;
                            $num_matches = preg_match_all('#(<\w+[^<>]*\s)(action|href|src|srcset)(=")([^"]*://[^"]*)("[^<>]*>)#i', $value, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
                            for ($j = 0; $j < $num_matches; $j++) {
                                $url = $matches[$j][4][0];
                                $offset = $matches[$j][4][1];

                                $start_tag = $matches[$j][1][0] . $matches[$j][2][0] . $matches[$j][3][0];
                                $end_tag = $matches[$j][5][0];
                                $remaining_tag = $start_tag . $end_tag;

                                $value_new .= substr($value, $last_offset, $offset - $last_offset);

                                // We don't want those to be relative, search engines need them to be absolute as they are to be used out-of-context
                                $preserved_url = false;
                                if (strpos($remaining_tag, 'rel="canonical"') !== false) {
                                    $preserved_url = true;
                                }
                                if (strpos($remaining_tag, 'hreflang="') !== false) {
                                    $preserved_url = true;
                                }

                                if (!$preserved_url) {
                                    $multi_urls = preg_split('#(\s+)#', $url, -1, PREG_SPLIT_DELIM_CAPTURE);
                                    $url = '';
                                    foreach ($multi_urls as $_url) {
                                        if (trim($_url) != '') {
                                            if ((in_array('relativeurls', $options)) && ($is_outside_root) && (substr($_url, 0, $current_relative_base_len) == $current_relative_base)) {
                                                $_url = substr($_url, $current_relative_base_len);
                                            } elseif (in_array('protocolrelativeurls', $options)) {
                                                $_url = preg_replace('#^' . (tacit_https() ? 'https' : 'http') . '://#i', '//', $_url);
                                            }
                                        }
                                        $url .= $_url;
                                    }
                                }

                                $value_new .= $url;

                                $last_offset = $offset + strlen($matches[$j][4][0]);
                            }
                            $value_new .= substr($value, $last_offset);
                            $value = $value_new;
                        }
                    }

                    if (in_array('selfclose', $options)) {
                        $value = str_replace(' />', '>', $value);
                    }

                    if (in_array('redundantclose', $options)) {
                        $value = preg_replace('#\n*</(li|option)>#i', '', $value);
                        $value = preg_replace('#\n*</p>(\s*<p)#i', '$1', $value);
                    }

                    if (in_array('quotes', $options)) {
                        $value = preg_replace('#(\s*\w+=)"([^\s`\'\"=<>]+)"#', '$1$2', $value);
                    }

                    if (in_array('cdata', $options)) {
                        $value = str_ireplace(['// <![CDATA[', '//]]>'], ['', ''], $value);
                    }

                    $value = trim($value);
                }
                break;

            case 'CDN_FILTER':
                $value = $param[count($param) - 2]->evaluate();
                if ((get_option('cdn_regexps') != '') && (get_option('cdn') != '') && (get_param_integer('keep_minify', 1) == 1)) {
                    $matches = [];
                    $bu = get_base_url();
                    $num_matches = preg_match_all('#(\s+(src|srcset|href))="' . preg_quote($bu, '#') . '/([^"]+)"#', $value, $matches);
                    $cdn_regexps = str_replace("\n", '|', get_option('cdn_regexps'));
                    for ($i = 0; $i < $num_matches; $i++) {
                        $_url = $matches[3][$i];
                        if (preg_match('#' . $cdn_regexps . '#', $_url) != 0) {
                            $url = $bu . '/' . html_entity_decode($_url, ENT_QUOTES);
                            $from = $matches[0][$i];
                            $to = $matches[1][$i] . '="' . htmlentities(cdn_filter($url), ENT_QUOTES) . '"';
                            $value = str_replace($from, $to, $value);
                        }
                    }
                }
                break;

            default:
                // Maybe a hook?
                static $extra_directives = null;
                if ($extra_directives === null) {
                    $extra_directives = [];
                    $hooks = find_all_hooks('systems', 'directives');
                    foreach (array_keys($hooks) as $hook) {
                        $extra_directives[$hook] = [];
                    }
                }
                if (isset($extra_directives[$name])) {
                    if (!isset($extra_directives[$name]['ob'])) {
                        require_code('hooks/systems/directives/' . filter_naughty_harsh($name));
                        $extra_directives[$name]['ob'] = object_factory('Hook_directive_' . filter_naughty_harsh($name));
                    }
                    $value = $extra_directives[$name]['ob']->run($param);
                } else {
                    trigger_error(do_lang('UNKNOWN_DIRECTIVE', escape_html($name)), E_USER_NOTICE);
                }
        }

        if (!empty($escaped)) {
            apply_tempcode_escaping($escaped, $value);
        }

        return $value;
    }

    // LANGUAGE STRINGS...

    $a = isset($param[0]) ? (is_object($param[0]) ? $param[0]->evaluate() : $param[0]) : null;
    $b = isset($param[1]) ? (is_object($param[1]) ? $param[1]->evaluate() : $param[1]) : null;
    $c = isset($param[2]) ? array_splice($param, 2) : null;
    if ($c !== null) {
        foreach ($c as $i => $cc) {
            if (is_object($cc)) {
                $c[$i] = $cc->evaluate();
            }
        }
    }
    static $dle = false;
    if (!$dle) {
        $dle = function_exists('do_lang');
    }
    $value = $dle ? do_lang($name, $a, $b, $c, $lang, false) : escape_html($name . ':' . (($a !== null) ? $a : '') . ',' . (($b !== null) ? $b : ''));
    if ($value === null) {
        trigger_error(do_lang('MISSING_LANG_STRING', escape_html($name)), E_USER_NOTICE);

        $value = '';
        if ($GLOBALS['XSS_DETECT']) {
            ocp_mark_as_escaped($value);
        }
        return $value;
    }
    if ((!empty($escaped)) && ($escaped !== [ENTITY_ESCAPED])) {
        apply_tempcode_escaping(array_diff($escaped, [ENTITY_ESCAPED]), $value); // Escape but without ENTITY_ESCAPED because we don't do that on language strings
    } elseif ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        $_p = $param[0]->evaluate();
        if ($_p == '1') {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_EMPTY(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        if ($param[0]->is_empty()) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_NON_EMPTY(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        if (!$param[0]->is_empty()) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_PASSED(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        $t = $param[0]->evaluate();
        if (isset($param['vars'][$t])) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_NON_PASSED(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        $t = $param[0]->evaluate();
        if (!isset($param['vars'][$t])) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_PASSED_AND_TRUE(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        $t = $param[0]->evaluate();
        if ((isset($param['vars'][$t])) && ($param['vars'][$t] !== false) && ($param['vars'][$t] === '1')) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_IF_NON_PASSED_OR_FALSE(string &$value, string $lang, array $escaped, array $param)
{
    if (isset($param[1])) {
        $t = $param[0]->evaluate();
        if ((!isset($param['vars'][$t])) || ($param['vars'][$t] === false) || ($param['vars'][$t] !== '1')) {
            $value = $param[1]->evaluate();
        }
    }
}

/**
 * Evaluate a particular Tempcode directive.
 *
 * @ignore
 *
 * @param  string $value Value to write into
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 */
function ecv_LOOP(string &$value, string $lang, array $escaped, array $param)
{
    // This code is mostly defunct, as the closure_loop function is normally used for performance reasons
    // (no need to keep re-binding, as closure_loop runs at a lower level)

    if (isset($param[0])) {
        $array_key = $param[0]->evaluate();
        if ((is_numeric($array_key)) || (strpos($array_key, ',') !== false) || (strpos($array_key, '=') !== false)) {
            $array = comma_list_str_to_arr($array_key);
        } else {
            if (!array_key_exists($param[0]->evaluate(), $param['vars'])) {
                trigger_error(do_lang('MISSING_TEMPLATE_PARAMETER', $param[0]->evaluate(), '???'), E_USER_NOTICE);
                return;
            }

            $array = array_key_exists($array_key, $param['vars']) ? $param['vars'][$array_key] : [];
            if (!is_array($array)) {
                $array = [];
            }
        }

        $value = '';
        if (array_key_exists(1 + 1, $param)) {
            $columns = $param[1]->evaluate();
            $row_starter = array_key_exists(2 + 1, $param) ? $param[2]->evaluate() : '<tr>';
            $row_terminator = array_key_exists(3 + 1, $param) ? $param[3]->evaluate() : '</tr>';
            $value .= $row_starter;

            // Sorting
            if (array_key_exists(4 + 1, $param)) {
                $sort_key = $param[4]->evaluate();

                $rev = ((array_key_exists(5 + 1, $param)) && ($param[5]->evaluate() == 'DESC'));
                if ($sort_key != '') {
                    sort_maps_by($array, $sort_key);
                }
                if ($rev) {
                    $array = array_reverse($array);
                }
            }
        }
        $last = count($param) - 2;
        $col = 0;
        $first = true;
        foreach ($array as $go_key => $go) {
            if (!is_array($go)) {
                $go = ['_loop_key' => make_string_tempcode(is_integer($go_key) ? strval($go_key) : $go_key), '_loop_var' => make_string_tempcode($go)]; // In case it's not a list of maps, but just a list
            }

            if ((isset($param[2])) && ($col % $columns == 0) && ($col != 0)) {
                $value .= $row_starter;
            }
            $ps = $go + $param['vars'] + ['_loop_key' => make_string_tempcode(is_integer($go_key) ? strval($go_key) : $go_key), '_i' => strval($col), '_first' => $first, '_last' => $col == count($array) - 1];
            $bound = $param[$last]->bind($ps, '');
            $value .= $bound->evaluate();
            ++$col;
            if ((isset($param[3])) && ($col % $columns == 0)) {
                $value .= $row_terminator;
            }
            $first = false;
        }
        if ((isset($param[2])) && ($col % $columns != 0)) {
            $value .= $row_terminator;
        }
    }
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PAGE_LINK(string $lang, array $escaped, array $param) : string
{
    if (isset($param[0])) {
        list($zone, $map, $hash) = page_link_decode(isset($param[0]->codename)/*faster than is_object*/ ? $param[0]->evaluate() : $param[0]);

        $skip = [];

        if (isset($param[1])) {
            $avoid_remap = !empty($param[1]);
            $skip_keep = !empty($param[2]);
            $keep_all = !empty($param[3]);
            if (isset($param[4])) {
                $skip = array_flip(explode('|', $param[4]));
            }
        } else {
            $avoid_remap = false;
            $skip_keep = false;
            $keep_all = false;
        }

        $value = _build_url($map, $zone, $skip, $keep_all, $avoid_remap, $skip_keep, $hash);
    } else {
        $value = get_current_page_link();
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SET(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    global $TEMPCODE_SETGET;

    if (isset($param[1])) {
        if ((count($param) == 2) || (isset($param[1], $param[1]->codename)/*faster than is_object*/)) {
            $TEMPCODE_SETGET[$param[0]] = $param[1];
        } else {
            $param_copy = $param;
            unset($param_copy[0]);
            $TEMPCODE_SETGET[$param[0]] = isset($param_copy[2]/*optimisation*/) ? implode(',', $param_copy) : $param_copy[1];
        }
    } else {
        $TEMPCODE_SETGET[$param[0]] = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_GET(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        global $TEMPCODE_SETGET;
        if (isset($TEMPCODE_SETGET[$param[0]])) {
            if (isset($TEMPCODE_SETGET[$param[0]]->codename)/*faster than is_object*/) {
                if ((isset($param[1])) && (((is_string($param[1])) && ($param[1] == '1')) || ((is_object($param[1])) && ($param[1]->evaluate() == '1')))) { // no-cache
                    $TEMPCODE_SETGET[$param[0]]->decache();
                    $value = $TEMPCODE_SETGET[$param[0]]->evaluate();
                    $TEMPCODE_SETGET[$param[0]]->decache();

                    if ((!has_solemnly_declared(I_UNDERSTAND_XSS)) && (empty($escaped))) {
                        $escaped = [ENTITY_ESCAPED];
                    }

                    if (!empty($escaped)) {
                        apply_tempcode_escaping($escaped, $value);
                    }
                    return $value;
                }

                $TEMPCODE_SETGET[$param[0]] = $TEMPCODE_SETGET[$param[0]]->evaluate();
            }

            $value = $TEMPCODE_SETGET[$param[0]];
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TERNARY(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[1])) {
        $value = ($param[0] == '1') ? $param[1] : (isset($param[2]) ? $param[2] : $value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IMG(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        if ((isset($GLOBALS['SITE_DB'])) && (function_exists('find_theme_image')) && (!$GLOBALS['IN_MINIKERNEL_VERSION']) && ($GLOBALS['FORUM_DRIVER'] !== null)) {
            $value = find_theme_image($param[0], !empty($param[3]) || running_script('upgrader'), false, (isset($param[2]) && $param[2] !== '') ? $param[2] : null, null, ((!empty($param[1])) && (get_forum_type() == 'cns')) ? $GLOBALS['FORUM_DB'] : $GLOBALS['SITE_DB']);
        } else {
            $path = 'themes/default/images/';
            $value = $path . $param[0] . '.png';
            if (!is_file(get_file_base() . '/' . $value)) {
                $value = $path . $param[0] . '.gif';
            }
            if (!is_file(get_file_base() . '/' . $value)) {
                $value = $path . $param[0] . '.svg';
            }

            if (running_script('install')) {
                $value = 'install.php?type=' . $value;
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IMG_INLINE(string $lang, array $escaped, array $param) : string
{
    if (running_script('themewizard')) {
        return ecv_IMG($lang, $escaped, $param);
    }

    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        if ((isset($GLOBALS['SITE_DB'])) && (function_exists('find_theme_image')) && (!$GLOBALS['IN_MINIKERNEL_VERSION']) && ($GLOBALS['FORUM_DRIVER'] !== null)) {
            $value = find_theme_image($param[0], true, true, @cms_empty_safe($param[2]) ? null : $param[2], null, ((!empty($param[1])) && (get_forum_type() == 'cns')) ? $GLOBALS['FORUM_DB'] : $GLOBALS['SITE_DB']);
        } else {
            $value = 'themes/default/images/' . $param[0] . '.png';
            if (!is_file(get_file_base() . '/' . $value)) {
                $value = 'themes/default/images/' . $param[0] . '.gif';
            }
            if (!is_file(get_file_base() . '/' . $value)) {
                $value = 'themes/default/images/' . $param[0] . '.svg';
            }
        }
        if ($value != '') {
            $file_path = ((substr($value, 0, 22) == 'themes/default/images/') ? get_file_base() : get_custom_file_base()) . '/' . $value;
            require_code('mime_types');
            $value = 'data:' . get_mime_type(get_file_extension($file_path), true) . ';base64,' . base64_encode(cms_file_get_contents_safe($file_path, FILE_READ_LOCK));
        } else {
            return ecv_IMG($lang, $escaped, $param);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IMG_MTIME(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        $path = find_theme_image($param[0], true, true);
        if ($path != '') {
            $full_path = get_custom_file_base() . '/' . $path;
            if (!is_file($full_path)) {
                $full_path = get_file_base() . '/' . $path;
            }
            $value = @strval(filemtime($full_path));
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IMG_WIDTH(string $lang, array $escaped, array $param) : string
{
    require_code('images');
    list($value,) = _symbol_image_dims($param);

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IMG_HEIGHT(string $lang, array $escaped, array $param) : string
{
    require_code('images');
    list(, $value) = _symbol_image_dims($param);

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Render an 'IMG_WIDTH'/'IMG_HEIGHT' symbol.
 *
 * @param  array $param Symbol parameters
 * @return array A pair: Image dimensions
 *
 * @ignore
 */
function _symbol_image_dims(array $param) : array
{
    if (!function_exists('imagecreatefromstring')) {
        return ['', ''];
    }

    $value = ['', ''];

    if (running_script('install')) {
        return $value;
    }

    if (!empty($param[0])) {
        $url = $param[0];
        if ($url == '') {
            return $value;
        }

        $cacheable = !empty($param[1]);

        if ($cacheable) {
            $cache = persistent_cache_get('IMAGE_DIMS');
            if (isset($cache[$url])) {
                return $cache[$url];
            }
        }

        $only_if_local = !empty($param[2]);

        $details = cms_getimagesize_url($url);

        if (($details !== false) && ($details[0] !== null) && ($details[1] !== null)) {
            $value = [strval($details[0]), strval($details[1])];
        }

        if ($cacheable) {
            $cache[$url] = $value;
            persistent_cache_set('IMAGE_DIMS', $cache);
        }
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_THEMEWIZARD_COLOR(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[2])) {
        global $TEMPCODE_SETGET;
        $TEMPCODE_SETGET[$param[1]] = $param[0];
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CSS_MODE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[2])) {
        global $TEMPCODE_SETGET;
        $param_copy = $param;
        unset($param_copy[0]);
        $TEMPCODE_SETGET[$param[0]] = implode(',', $param_copy);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_FORUM(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        $value = (get_forum_type() == 'none') ? '0' : '1';
    } else {
        $value = has_no_forum() ? '0' : '1';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_(string $lang, array $escaped, array $param) : string // A Tempcode comment
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_REFRESH(string $lang, array $escaped, array $param) : string
{
    // This symbol is only used by HTML_HEAD.tpl

    // Is this refreshing?
    global $REFRESH_URL;
    if (!empty($REFRESH_URL[0])) { // The page itself has actually told it to refresh itself
        if (!isset($REFRESH_URL[1])) {
            $REFRESH_URL[1] = 1.0;
        }
        $refresh = do_template('META_REFRESH_LINE', ['_GUID' => '6ee20694dfa474f160481a3ab5331d87', 'URL' => $REFRESH_URL[0], 'TIME' => float_to_raw_string($REFRESH_URL[1], 2, true)]);
    } else {
        $refresh = new Tempcode();
    }
    $value = $refresh->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FEEDS(string $lang, array $escaped, array $param) : string
{
    // Feeds
    if ((get_option('is_on_rss', true) === '1') && (addon_installed('syndication'))) {
        $feeds = new Tempcode();
        if (addon_installed('news')) {
            $feeds->attach(do_template('RSS_HEADER', ['_GUID' => '53e135b04502d6df64f1570b61310f30', 'FEED_URL' => find_script('backend') . '?mode=news', 'TITLE' => do_lang('NEWS')]));
        }
        foreach ($GLOBALS['FEED_URLS'] as $feed) {
            if (substr($feed['url'], 0, 1) == '?') {
                $feed['url'] = find_script('backend') . $feed['url'];
            }
            $map = ['_GUID' => '371ef26788e14c17b6ad2bab9f9ffd1c', 'FEED_URL' => $feed['url'], 'TITLE' => $feed['title']];
            $feeds->attach(do_template('RSS_HEADER', $map));
        }
        $value = $feeds->evaluate();
    } else {
        $value = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_METADATA(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        global $METADATA, $BREADCRUMB_SET_SELF;

        switch ($param[0]) {
            case 'site_newestmember':
                // Getting
                $value = get_value('cns_newest_member_username');
                if ($value === null) {
                    $value = '';
                }
                break;
            case 'site_nummembers':
                // Getting
                if ($GLOBALS['FORUM_DRIVER'] !== null) {
                    $value = strval($GLOBALS['FORUM_DRIVER']->get_num_members());
                }
                break;
            case 'forum_numtopics':
                // Getting
                if ($GLOBALS['FORUM_DRIVER'] !== null) {
                    $value = strval($GLOBALS['FORUM_DRIVER']->get_num_topics());
                }
                break;
            case 'forum_numposts':
                // Getting
                if ($GLOBALS['FORUM_DRIVER'] !== null) {
                    $value = strval($GLOBALS['FORUM_DRIVER']->get_num_forum_posts());
                }
                break;

            case 'meta_description':
                if ((!empty($param[1])) && (isset($param[2]))) {
                    // Getting specified
                    list(, $value) = seo_meta_get_for($param[1], $param[2]);
                } else {
                    // Getting contextual
                    global $SEO_DESCRIPTION;
                    if (($SEO_DESCRIPTION === null) || ($SEO_DESCRIPTION == '')) {
                        if (!@cms_empty_safe($METADATA['description'])) {
                            $value = strip_comcode($METADATA['description']);
                        } else {
                            $value = get_option('description');
                        }
                    } else {
                        $value = $SEO_DESCRIPTION;
                    }
                }
                break;

            case 'raw_keywords':
                // Getting
                global $SEO_KEYWORDS;
                if ($SEO_KEYWORDS === null) {
                    $SEO_KEYWORDS = [];
                }
                $keywords_array = $SEO_KEYWORDS;
                $value = implode(',', array_unique($keywords_array));
                break;

            case 'keywords':
                if ((!empty($param[1])) && (isset($param[2]))) {
                    // Getting specified
                    list($value,) = seo_meta_get_for($param[1], $param[2]);
                } else {
                    // Getting contextual
                    global $SEO_KEYWORDS;
                    $keywords = get_option('keywords');
                    if ($SEO_KEYWORDS === null) {
                        $SEO_KEYWORDS = [];
                    }
                    $keywords_array = $SEO_KEYWORDS;
                    if ($keywords != '') {
                        $keywords_array = array_merge($keywords_array, array_map('trim', explode(',', $keywords)));
                    }
                    foreach ($keywords_array as &$keyword) {
                        $_keyword = do_lang('TAG_OVERRIDE_' . preg_replace('#[^\w]#', '_', $keyword), null, null, null, null, false);
                        if ($_keyword !== null) {
                            $keyword = $_keyword;
                        }
                    }
                    $value = implode(',', array_unique($keywords_array));
                }
                break;

            case 'lang':
                if (isset($param[1])) {
                    // Setting override
                    $METADATA[$param[0]] = $param[1];
                } else {
                    if (!empty($METADATA[$param[0]])) {
                        // Getting override
                        $value = $METADATA[$param[0]];
                    } else {
                        // Getting default
                        $value = user_lang();
                    }
                }
                break;

            case 'breadcrumb_self':
                if (isset($param[1])) {
                    // Setting
                    $BREADCRUMB_SET_SELF = $param[1];
                } else {
                    // Getting (do nothing)
                }
                break;

            default:
                if (isset($param[1])) {
                    // Setting a generic...

                    // FUDGE Special case
                    $matches = [];
                    if (($param[0] == 'image') && (strpos($param[1], '?') !== false) && (preg_match('#^' . preg_quote(find_script('attachment'), '#') . '\?id=(\d+)#', $param[1], $matches) != 0)) {
                        require_code('attachments');
                        if (!has_attachment_access($GLOBALS['FORUM_DRIVER']->get_guest_id(), intval($matches[1]))) {
                            break;
                        }
                    }

                    $METADATA[$param[0]] = $param[1];
                } else {
                    // Getting a generic...

                    if (isset($METADATA[$param[0]])) {
                        $_value = $METADATA[$param[0]];

                        if ($_value === null) {
                            $_value = '';
                        }

                        $value = strval($_value);

                        // Special case
                        if ($param[0] != 'image') {
                            if ($param[0] == 'title' || $param[0] == 'description') {
                                $value = strip_comcode($value);
                            }
                        }
                    }
                }
                break;
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_KEEP(string $lang, array $escaped, array $param) : string
{
    // What needs preserving in the URL
    $value = keep_symbol($param);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * String to tack onto URL to keep 'keep_' parameters.
 *
 * @param  array $param Parameters passed to the symbol (0=whether this starts off the query string, 1=force session append even if it's also available a session cookie e.g. when put into download manager)
 * @return string The result
 */
function keep_symbol(array $param) : string
{
    $value = '';

    global $HAS_NO_KEEP_CONTEXT;
    if ($HAS_NO_KEEP_CONTEXT) {
        return $value;
    }

    $get_vars = $_GET;
    if ((!empty($param[1])) && (get_bot_type() === null) && (!isset($get_vars['keep_session']))) {
        $get_vars['keep_session'] = get_session_id();
    }

    $first = !empty($param[0]);
    foreach ($get_vars as $key => $val) {
        if ((@$key[0] == 'k') && (substr($key, 0, 5) === 'keep_') && ((!skippable_keep($key, $val)) || (($key === 'keep_session') && (get_bot_type() === null) && (!empty($param[1])))) && (is_string($val))) {
            $value .= ($first ? '?' : '&') . urlencode($key) . '=' . cms_urlencode($val);
            $first = false;
        }
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BROWSER(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[1])) {
        $q = false;
        foreach (explode('|', $param[0]) as $browser) {
            $q = browser_matches($browser);
            if ($q) {
                break;
            }
        }
        $value = $q ? $param[1] : (isset($param[2]) ? $param[2] : '');
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_REQUIRE_JAVASCRIPT(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        global $JAVASCRIPTS;
        if (!isset($JAVASCRIPTS[$param[0]])) {
            require_javascript($param[0]);
        }
    }

    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FACILITATE_AJAX_BLOCK_CALL(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        require_code('blocks');
        $map = comma_list_str_to_arr($param[0]);
        $_block_constraints = block_params_to_block_signature($map);
        if (isset($param[1])) {
            $_block_constraints = array_merge($_block_constraints, comma_list_str_to_arr($param[1]));
            ksort($_block_constraints);
        }
        $block_constraints = comma_list_arr_to_str($_block_constraints);

        // Store permissions
        $pass = false;
        $_safelisted_blocks = get_value('safelisted_blocks');
        if (!empty($_safelisted_blocks)) {
            $safelisted_blocks = explode(',', $_safelisted_blocks);
            if (in_array($map['block'], $safelisted_blocks)) {
                $pass = true;
            }
        }
        if (!$pass) {
            $_auth_key = $GLOBALS['SITE_DB']->query_select('temp_block_permissions', ['id', 'p_time'], [
                'p_session_id' => is_guest() ? '' : get_session_id(),
                'p_block_constraints' => $block_constraints,
            ], '', 1);
            if (!isset($_auth_key[0])) {
                $auth_key = $GLOBALS['SITE_DB']->query_insert('temp_block_permissions', [
                    'p_session_id' => is_guest() ? '' : get_session_id(),
                    'p_block_constraints' => $block_constraints,
                    'p_time' => time(),
                ], true);
            } else {
                $auth_key = $_auth_key[0]['id'];
                if (time() - $_auth_key[0]['p_time'] > 100) {
                    $GLOBALS['SITE_DB']->query_update('temp_block_permissions', ['p_time' => time()], [
                        'p_session_id' => is_guest() ? '' : get_session_id(),
                        'p_block_constraints' => $block_constraints,
                    ], '', 1);
                }
            }
        }

        $keep = symbol_tempcode('KEEP');
        $self_url_encoded = static_evaluate_tempcode(protect_url_parameter(get_self_url(true)));
        $value = find_script('snippet') . '?snippet=block&auth_key=' . urlencode(strval($auth_key)) . '&self_url=' . urlencode($self_url_encoded) . '&block_map=' . urlencode($param[0]) . $keep->evaluate();
        if (get_param_string('utheme', null) !== null) {
            $value .= '&utheme=' . urlencode($GLOBALS['FORUM_DRIVER']->get_theme());
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv__GET(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        $value = get_param_string($param[0], isset($param[1]) ? $param[1] : '', INPUT_FILTER_GET_COMPLEX);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_STRIP_TAGS(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[0])) {
        if (strpos($param[0], '<') === false) { // optimisation
            $value = $param[0];
        } else {
            $inclusion_list = empty($param[2]);
            $value = cms_strip_tags($param[0], isset($param[1]) ? $param[1] : '', $inclusion_list);
            if ($inclusion_list) {
                $value = str_replace('"', '&quot;', $value); // So can be used inside HTML attributes
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TRUNCATE_LEFT(string $lang, array $escaped, array $param) : string
{
    if (isset($param[0])) {
        $len = (!empty($param[1])) ? intval($param[1]) : 60;
        $generate_tooltip = (!empty($param[2]));
        if (@cms_empty_safe($param[3])) {
            $is_html = null;
        } else {
            $is_html = ($param[3] == '1');
        }
        $literal_pos = (!empty($param[4]));
        $grammar_completeness_tolerance = (!empty($param[5])) ? floatval($param[5]) : 0.0;

        $value = generate_truncation($param[0], 'left', $len, $generate_tooltip, $is_html, $literal_pos, $grammar_completeness_tolerance);
    } else {
        $value = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TRUNCATE_SPREAD(string $lang, array $escaped, array $param) : string
{
    if (isset($param[0])) {
        $len = (!empty($param[1])) ? intval($param[1]) : 60;
        $generate_tooltip = (!empty($param[2]));
        if (@cms_empty_safe($param[3])) {
            $is_html = null;
        } else {
            $is_html = ($param[3] == '1');
        }
        $literal_pos = (!empty($param[4]));
        $grammar_completeness_tolerance = (!empty($param[5])) ? floatval($param[5]) : 0.0;

        $value = generate_truncation($param[0], 'spread', $len, $generate_tooltip, $is_html, $literal_pos, $grammar_completeness_tolerance);
    } else {
        $value = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PARAGRAPH(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        $is_blocky_already = (trim($param[0]) == '') || (preg_match('#<(p|div|form|ul|ol|dl|blockquote|h1|h2|h3|h4|h5|h6|table|iframe)(\s.*)?' . '>#', $param[0]) != 0);

        if ($is_blocky_already) {
            $value .= $param[0];
        } else {
            $class = isset($param[1]) ? $param[1] : '';
            if ($class == '') {
                $value .= '<p>';
            } else {
                $value .= '<p class="' . escape_html($class) . '">';
            }
            $value .= $param[0];
            $value .= '</p>';
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_COMMA_LIST_GET(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[1])) {
        require_code('blocks');
        $values = comma_list_str_to_arr($param[0], !empty($param[2]));
        $value = isset($values[$param[1]]) ? $values[$param[1]] : '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_EMPTY(string $lang, array $escaped, array $param) : string
{
    $value = '1';

    if (isset($param[0])) {
        $value = ($param[0] === '') ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_NON_EMPTY(string $lang, array $escaped, array $param) : string
{
    $value = '0';

    if (isset($param[0])) {
        $value = ($param[0] !== '') ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CDN_FILTER(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $value = cdn_filter($param[0]);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LOAD_PANEL(string $lang, array $escaped, array $param) : string
{
    $temp_array = [];
    handle_symbol_preprocessing([$escaped, TC_SYMBOL, 'LOAD_PANEL', $param], $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases

    foreach ($param as $i => $p) {
        if (is_object($p)) {
            $param[$i] = $p->evaluate();
        }
    }

    global $PANELS_CACHE;
    if (strpos($param[0], ':') !== false) {
        $param = array_reverse(explode(':', $param[0], 2));
    }
    if (substr($param[0], 0, 6) == 'panel_') {
        $param[0] = substr($param[0], 6);
    }
    $sr = serialize($param);
    $value = array_key_exists($sr, $PANELS_CACHE) ? $PANELS_CACHE[$sr] : '';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_JS_ON(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[0])) {
        $value = has_js() ? $param[0] : (isset($param[1]) ? $param[1] : '');
    } else {
        $value = has_js() ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ZONE(string $lang, array $escaped, array $param) : string
{
    $value = get_zone_name();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PAGE(string $lang, array $escaped, array $param) : string
{
    $value = get_page_name();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HEADER_TEXT(string $lang, array $escaped, array $param) : string
{
    global $ZONE, $SHORT_TITLE, $DISPLAYED_TITLE;
    if ($ZONE === null) {
        load_zone_data();
    }
    if ($SHORT_TITLE === null) { // Take from either zone header or screen title
        if ($DISPLAYED_TITLE !== null) {
            $_displayed_title = $DISPLAYED_TITLE->evaluate();
        }
        if (($DISPLAYED_TITLE !== null) && (strip_tags($_displayed_title) != '')) {
            $value = strip_html(preg_replace('#<a[^<>]*>.*</a>#U', '', $_displayed_title)); // The regexp is to remove possible FRACTIONAL_EDIT.tpl link
        } else {
            if ($ZONE !== null) {
                $value = get_translated_text($ZONE['zone_header_text']);
            }
        }
    } else { // Take from short title
        $comcodeless = strip_comcode($SHORT_TITLE); // This is not HTML

        // Strip 'Welcome to' off if it's there
        $value = cms_preg_replace_safe('#' . preg_quote(do_lang('WELCOME_TO_STRIPPABLE') . ' ' . get_site_name(), '#') . '([^\-]+\s*-\s*)?#', '', $comcodeless);

        // Strip site name off it it's there (it'll be put on in the templates, so we don't want it twice)
        $stub = get_site_name() . ' - ';
        if (substr($value, strlen($stub)) === $stub) {
            $value = substr($value, strlen($stub));
        }
        if ($value == get_site_name()) {
            $value = '';
        }
    }
    $value = trim($value);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CANONICAL_URL(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        // Used to set, which is done in pre-processing
        return '';
    }

    $value = get_canonical_url();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SHOW_HEADER(string $lang, array $escaped, array $param) : string
{
    $value = (is_wide_high() == 1) ? '0' : '1';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SHOW_FOOTER(string $lang, array $escaped, array $param) : string
{
    $value = (is_wide_high() == 1) ? '0' : '1';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_WIDE(string $lang, array $escaped, array $param) : string
{
    $value = (is_wide() == 1) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_WIDE_HIGH(string $lang, array $escaped, array $param) : string
{
    $value = (is_wide_high() == 1) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LOGO_URL(string $lang, array $escaped, array $param) : string
{
    $value = get_logo_url();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HELPER_PANEL_TUTORIAL(string $lang, array $escaped, array $param) : string
{
    if (is_wide_high() == 1) { // No helper panels on wide high screens
        return '';
    }

    if ($GLOBALS['HELPER_PANEL_TUTORIAL'] === null) {
        set_helper_panel_tutorial('');
    }
    if (get_option('show_docs') == '0') {
        set_helper_panel_tutorial('');
    }
    $value = $GLOBALS['HELPER_PANEL_TUTORIAL'];

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HELPER_PANEL_TEXT(string $lang, array $escaped, array $param) : string
{
    if (is_wide_high() == 1) { // No helper panels on wide high screens
        return '';
    }

    if ($GLOBALS['HELPER_PANEL_TEXT'] === null) {
        $GLOBALS['HELPER_PANEL_TEXT'] = '';
    }
    $value = is_object($GLOBALS['HELPER_PANEL_TEXT']) ? $GLOBALS['HELPER_PANEL_TEXT']->evaluate() : $GLOBALS['HELPER_PANEL_TEXT'];

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MESSAGES_TOP(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ($GLOBALS['ATTACHED_MESSAGES'] !== null) {
        $value = static_evaluate_tempcode($GLOBALS['ATTACHED_MESSAGES']);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MESSAGES_BOTTOM(string $lang, array $escaped, array $param) : string
{
    // Extra stuff we can tag on (like messages)
    $messages_bottom = new Tempcode();
    if (get_option('site_closed') != '0') {
        require_code('global4');
        attach_message_site_closed($messages_bottom);
    }
    if ($GLOBALS['IS_ACTUALLY_ADMIN']) {
        require_code('global4');
        attach_message_su($messages_bottom);
    }
    if (get_param_string('special_page_type', '') == 'memory') {
        require_code('view_modes');
        attach_message_memory_usage($messages_bottom);
    }
    $value = $messages_bottom->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LATE_MESSAGES(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ($GLOBALS['LATE_ATTACHED_MESSAGES'] !== null) {
        $value = static_evaluate_tempcode($GLOBALS['LATE_ATTACHED_MESSAGES']);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BREADCRUMBS(string $lang, array $escaped, array $param) : string
{
    $show_self = empty($param[0]) || $param[0] == '1';
    if (!$show_self) {
        $GLOBALS['BREADCRUMBS'] = null;
    }
    $value = static_evaluate_tempcode(breadcrumbs($show_self));

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_SU(string $lang, array $escaped, array $param) : string
{
    $value = '0';
    if (!is_guest()) {
        $value = ((get_option('show_su') == '1') && (has_privilege(get_member(), 'assume_any_member'))) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_STAFF_ACTIONS(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!is_guest()) {
        // Different types of page type for staff (debug view, etc)
        if ((get_option('show_staff_page_actions') == '1') && (has_privilege(get_member(), 'view_profiling_modes')) && (!has_interesting_post_fields())) { // We count POST because we don't want to allow double submits
            require_code('site2');
            $value = get_staff_actions_list();
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TRIM(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        $value = $param[0];

        if ($GLOBALS['INJECT_HIDDEN_TEMPLATE_NAMES']) {
            $value = strip_invisible_output_encoding($value);
        }

        if ($value !== '') {
            if ((strpos($value, '<') === false) && (strpos($value, '&') === false)) {
                if ($GLOBALS['XSS_DETECT']) {
                    $is_escaped = ocp_is_escaped($value);
                }

                $value = trim($value);

                if ($GLOBALS['XSS_DETECT']) {
                    if ($is_escaped) {
                        ocp_mark_as_escaped($value);
                    }
                }
            } else {
                $value = cms_trim($param[0], !isset($param[1]) || $param[1] === '1');
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Trim some text, supporting removing HTML white-space also.
 *
 * @param  string $text Input text
 * @param  boolean $try_hard Whether to keep doing it, while it changes (if complex mixtures are on the end)
 * @return string The result text
 */
function cms_trim(string $text, bool $try_hard = true) : string
{
    if ((preg_match('#[<&]#', $text) === 0) && (memory_get_usage() > 1024 * 1024 * 40)) {
        return trim($text); // Don't have enough memory
    }

    if ($GLOBALS['XSS_DETECT']) {
        $is_escaped = ocp_is_escaped($text);
    }

    // Intentionally not using regexps, as actually using substr is a lot faster and uses much less memory

    do {
        $before = $text;
        $c = substr($text, 0, 1);
        if ($c === '<') {
            if (cms_strtolower_ascii(substr($text, 1, 1)) === 'b') {
                if (cms_strtolower_ascii(substr($text, 0, 6)) === '<br />') {
                    $text = substr($text, 6);
                }
                if (cms_strtolower_ascii(substr($text, 0, 5)) === '<br/>') {
                    $text = substr($text, 5);
                }
                if (cms_strtolower_ascii(substr($text, 0, 4)) === '<br>') {
                    $text = substr($text, 4);
                }
            }
        } elseif ($c == '&') {
            if (cms_strtolower_ascii(substr($text, 0, 6)) === '&nbsp;') {
                $text = substr($text, 6);
            }
        }
        $text = ltrim($text);
    } while (($try_hard) && ($before !== $text));
    do {
        $before = $text;
        $c = substr($text, -1, 1);
        if ($c === '>') {
            if (cms_strtolower_ascii(substr($text, -6)) === '<br />') {
                $text = substr($text, 0, -6);
            }
            if (cms_strtolower_ascii(substr($text, -5)) === '<br/>') {
                $text = substr($text, 0, -5);
            }
            if (cms_strtolower_ascii(substr($text, -4)) === '<br>') {
                $text = substr($text, 0, -4);
            }
        } elseif ($c == ';') {
            if (cms_strtolower_ascii(substr($text, -6)) === '&nbsp;') {
                $text = substr($text, 0, -6);
            }
        }
        $text = rtrim($text);
    } while (($try_hard) && ($before !== $text));

    if ($GLOBALS['XSS_DETECT']) {
        if ($is_escaped) {
            ocp_mark_as_escaped($text);
        }
    }

    return $text;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CPF_VALUE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!@cms_empty_safe($param[0])) {
        $_value = '';

        $member_id = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();

        if (is_numeric($param[0])) {
            require_code('cns_members');
            $custom_fields = cns_get_all_custom_fields_match_member($member_id);
            foreach ($custom_fields as $custom_field) {
                if ($custom_field['FIELD_ID'] == $param[0]) {
                    $_value = $custom_field[empty($param[2]) ? 'RAW' : 'RENDERED'];
                }
            }
        } elseif ((substr($param[0], 0, 2) == 'm_') && (stripos($param[0], 'hash') === false) && (stripos($param[0], 'salt') === false)) {
            $_value = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, $param[0]);
        } else {
            if (preg_match('#^\w*$#', $param[0]) != 0) {
                $_value = get_cms_cpf($param[0], ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : null);
            } else {
                require_code('cns_members');
                $cpf_id = find_cpf_field_id($param[0]);
                if ($cpf_id !== null) {
                    $custom_fields = cns_get_all_custom_fields_match_member($member_id);
                    foreach ($custom_fields as $custom_field) {
                        if ($custom_field['FIELD_ID'] == strval($cpf_id)) {
                            $_value = $custom_field[empty($param[2]) ? 'RAW' : 'RENDERED'];
                        }
                    }
                }
            }
        }

        if (!is_string($_value)) {
            $value = ($_value === null) ? '' : strval($_value);
        } else {
            $value = $_value;
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BANNER(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (addon_installed('banners')) {
        global $SITE_INFO;
        $is_on_banners = (!empty($param[1])) || (((!has_privilege(get_member(), 'banner_free')) || (($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member())) && (get_option('admin_banners') == '1')) || ($GLOBALS['CURRENT_SHARE_USER'] !== null)));
        if (!empty($SITE_INFO['throttle_bandwidth_registered'])) {
            $views_till_now = intval(get_value('page_views'));
            $bandwidth_allowed = $SITE_INFO['throttle_bandwidth_registered'];
            $total_bandwidth = intval(get_value('download_bandwidth'));
            if ($bandwidth_allowed * 1024 * 1024 >= $total_bandwidth) {
                $is_on_banners = false;
            }
        }
        if ($is_on_banners) {
            require_code('banners');

            $b_type = isset($param[0]) ? $param[0] : '';
            if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) { // Normal operation
                $width = empty($param[1]) ? null : intval($param[1]);
                $height = empty($param[2]) ? null : intval($param[2]);
                $region = (!empty($param[3])) ? $param[3] : null;
                $name = (!@cms_empty_safe($param[4])) ? $param[4] : null;
                $_value = banners_script(true, '', $name, $b_type, '', $width, $height, $region);
                $value = trim($_value->evaluate());
            } else { // Been told to behave statically
                $value = 'Banner goes here';
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_AVATAR(string $lang, array $escaped, array $param) : string
{
    $value = $GLOBALS['FORUM_DRIVER']->get_member_avatar_url(((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_GUEST(string $lang, array $escaped, array $param) : string
{
    if ((isset($param[0])) && (is_numeric($param[0]))) {
        $value = (is_guest(intval($param[0]))) ? '1' : '0';
    } else {
        $value = is_guest() ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MEMBER(string $lang, array $escaped, array $param) : string
{
    if (empty($param[0])) {
        $value = strval(get_member());
    } else {
        $member_id = $GLOBALS['FORUM_DRIVER']->get_member_from_username($param[0]);
        $value = ($member_id === null) ? '' : strval($member_id);
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_REQUIRE_CSS(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        require_css($param[0]);
    }

    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_HTTPAUTH_LOGIN(string $lang, array $escaped, array $param) : string
{
    $value = is_httpauth_login() ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MEMBER_PROFILE_URL(string $lang, array $escaped, array $param) : string
{
    $member_id = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member();
    $value = $GLOBALS['FORUM_DRIVER']->member_profile_url($member_id, true);
    if (is_object($value)) {
        $value = $value->evaluate();
    }
    if ($value === null) {
        $value = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_USERNAME(string $lang, array $escaped, array $param) : string
{
    $member_id = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member();
    $value = $GLOBALS['FORUM_DRIVER']->get_username($member_id, !empty($param[1]));

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DISPLAYED_USERNAME(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $value = get_displayname($param[0]);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CYCLE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        global $CYCLES;
        if (!isset($CYCLES[$param[0]])) {
            $CYCLES[$param[0]] = 0;
        }
        if (!isset($param[1])) { // If we can't find a param simply return the index. Poor-mans cycle reader.
            $value = strval($CYCLES[$param[0]]);
        } else { // Cycle
            if (count($param) == 2) {
                $param = array_merge([$param[0]], explode(',', $param[1]));
            }

            ++$CYCLES[$param[0]];
            if (!array_key_exists($CYCLES[$param[0]], $param)) {
                $CYCLES[$param[0]] = 1;
            }
            $value = $param[$CYCLES[$param[0]]];
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_THUMBNAIL(string $lang, array $escaped, array $param) : string
{
    if (empty($param[0])) {
        return '';
    }

    $orig_url = $param[0]; // Source for thumbnail generation

    if (empty($param[1])) {
        $dimensions = null;
    } else {
        $dimensions = $param[1];
    }

    if (empty($param[2])) { // Where we are saving to
        //$output_dir = dirname(rawurldecode(preg_replace('#' . preg_quote(get_custom_base_url() . '/', '#') . '#', '', $orig_url)));  We used to try and save into the same dir as the source image, but actually that's pretty messy
        $output_dir = 'uploads/auto_thumbs';
    } else {
        $output_dir = $param[2];
        if (strpos($output_dir, '/') === false) {
            $output_dir = 'uploads/' . $output_dir;
        }

        if (!is_dir(get_custom_file_base() . '/' . $output_dir)) {
            $output_dir = 'uploads/auto_thumbs';
        }
    }

    if (empty($param[3])) { // We can take a parameter that hints what filename to save with (useful to avoid filename collisions within the thumbnail filename subspace). Otherwise we based on source's filename
        $filename = null;
    } else {
        $filename = $param[3];
    }

    if (empty($param[4])) {
        $fallback_image = null;
    } else {
        $fallback_image = $param[4];
    }

    if (empty($param[5])) {
        $algorithm = 'box';
    } else {
        $algorithm = $param[5];
    }

    if (empty($param[6])) {
        $where = 'both';
    } else {
        $where = $param[6];
    }

    if (empty($param[7])) {
        $background = null;
    } else {
        $background = $param[7];
    }

    $only_make_smaller = !empty($param[8]);

    require_code('images');
    require_code('images2');

    $value = convert_image_plus($orig_url, $dimensions, $output_dir, $filename, $fallback_image, $algorithm, $where, $background, $only_make_smaller);

    if (url_is_local($value)) {
        $value = get_custom_base_url() . '/' . $value;
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_IN_GROUP(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        if (in_array($param[count($param) - 1], ['', 'primary', 'secondary'])) {
            $last_param = $param[count($param) - 1];
            unset($param[count($param) - 1]);
        } else {
            $last_param = '';
        }

        $member_id = get_member();
        $new_param = '';
        $param_2 = [];
        foreach ($param as $group) {
            if ((substr($group, 0, 1) == '@') && (is_numeric(substr($group, 1)))) {
                $member_id = intval(substr($group, 1));
            } else {
                $param_2 = array_merge($param_2, explode(',', $group));
            }
        }
        foreach ($param_2 as $group) {
            if ($new_param != '') {
                $new_param .= ',';
            }
            $new_param .= $group;
        }

        if ($last_param == 'primary') {
            $member_row = $GLOBALS['FORUM_DRIVER']->get_member_row($member_id);
            $real_group_list = [$GLOBALS['FORUM_DRIVER']->mrow_primary_group($member_row)];
        } elseif ($last_param == 'secondary') {
            $real_group_list = $GLOBALS['FORUM_DRIVER']->get_members_groups($member_id);
            $member_row = $GLOBALS['FORUM_DRIVER']->get_member_row($member_id);
            $real_group_list = array_diff($real_group_list, [$GLOBALS['FORUM_DRIVER']->mrow_primary_group($member_row)]);
        } else {
            $real_group_list = $GLOBALS['FORUM_DRIVER']->get_members_groups($member_id);
        }
        require_code('selectcode');
        $value = (!empty(array_intersect(selectcode_to_idlist_using_memory($new_param, $GLOBALS['FORUM_DRIVER']->get_usergroup_list()), $real_group_list))) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_STAFF(string $lang, array $escaped, array $param) : string
{
    if ((isset($GLOBALS['FORUM_DRIVER'])) && (function_exists('get_member'))) {
        $value = $GLOBALS['FORUM_DRIVER']->is_staff(((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member()) ? '1' : '0';
    } else {
        $value = '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_ADMIN(string $lang, array $escaped, array $param) : string
{
    if ((isset($GLOBALS['FORUM_DRIVER'])) && (function_exists('get_member'))) {
        $value = $GLOBALS['FORUM_DRIVER']->is_super_admin(((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member()) ? '1' : '0';
    } else {
        $value = '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PHOTO(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    $value = $GLOBALS['FORUM_DRIVER']->get_member_photo_url(((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CNS_RANK_IMAGE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (addon_installed('cns_forum') && (get_forum_type() == 'cns')) {
        require_code('cns_groups');
        $rank_images = new Tempcode();
        $member_id = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member();
        $posters_groups = $GLOBALS['FORUM_DRIVER']->get_members_groups($member_id, true);
        foreach ($posters_groups as $group) {
            $rank_image = cns_get_group_property($group, 'rank_image');
            $group_leader = cns_get_group_property($group, 'group_lead_member');
            $group_name = cns_get_group_name($group);
            $rank_image_pri_only = cns_get_group_property($group, 'rank_image_pri_only');
            if (($rank_image != '') && (($rank_image_pri_only == 0) || ($group == $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_primary_group')))) {
                $rank_images->attach(do_template('CNS_RANK_IMAGE', ['_GUID' => '513032ef4693abc353f9934e8799943b', 'USERNAME' => $GLOBALS['FORUM_DRIVER']->get_username($member_id), 'GROUP_NAME' => $group_name, 'IMG' => $rank_image, 'IS_LEADER' => $group_leader == $member_id]));
            }
        }
        $value = $rank_images->evaluate();
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_URL_FOR_GET_FORM(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $url_bits = parse_url($param[0]);
        if (isset($url_bits['scheme'])) {
            $value = $url_bits['scheme'] . '://' . (isset($url_bits['host']) ? $url_bits['host'] : get_base_url_hostname());
            if ((isset($url_bits['port'])) && ($url_bits['port'] != 80)) {
                $value .= ':' . strval($url_bits['port']);
            }
        }
        if (isset($url_bits['path'])) {
            $value .= $url_bits['path'];
        }
        if (!empty($url_bits['fragment'])) {
            $value .= '#' . $url_bits['fragment'];
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HIDDENS_FOR_GET_FORM(string $lang, array $escaped, array $param) : string
{
    $_value = new Tempcode();
    $url_bits = parse_url($param[0]);
    if (!@cms_empty_safe($url_bits['query'])) {
        foreach (explode('&', $url_bits['query']) as $exp) {
            $parts = explode('=', $exp, 2);
            if (count($parts) == 2) {
                if ((!in_array($parts[0], $param)) && (!in_array(preg_replace('#\[\d+\]#', '[]', $parts[0]), $param)) && (!in_array(preg_replace('#\d+#', '*', $parts[0]), $param)) && (!in_array(preg_replace('#filter_\w+#', 'filter_*', $parts[0]), $param))) {
                    $_value->attach(form_input_hidden($parts[0], urldecode($parts[1])));
                }
            }
        }
    }
    $value = $_value->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NOTIFICATIONS_ENABLED(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        require_code('notifications');
        $value = notifications_enabled((isset($param[1])) ? $param[1] : get_page_name(), $param[0]) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NOTIFICATIONS_AVAILABLE(string $lang, array $escaped, array $param) : string
{
    $value = '1';
    if (!empty($param[0])) {
        $notification_code = $param[0];

        require_code('notifications');

        if (notification_locked_down($notification_code) === 0) {
            $value = '0';
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MAKE_RELATIVE_DATE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[0])) && (is_numeric($param[0]))) {
        if ((get_option('use_contextual_dates') == '0') && (empty($param[1]))) {
            $value = get_timezoned_date_time(intval($param[0]));
        } else {
            if (!empty($param[2])) {
                $value = display_time_period(intval($param[0]) - time());
            } else {
                $value = display_time_period(time() - intval($param[0]));
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LOAD_PAGE(string $lang, array $escaped, array $param) : string
{
    $temp_array = [];
    handle_symbol_preprocessing([$escaped, TC_SYMBOL, 'LOAD_PAGE', $param], $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases

    foreach ($param as $i => $p) {
        if (is_object($p)) {
            $param[$i] = $p->evaluate();
        }
    }

    global $PAGES_CACHE;
    if (strpos($param[0], ':') !== false) {
        $param = array_reverse(explode(':', $param[0], 2));
    }
    $_value = $PAGES_CACHE[serialize($param)];
    $value = $_value->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_RUNNING_SCRIPT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!@cms_empty_safe($param[0])) {
        $value = running_script($param[0]) ? '1' : '0';
    } else {
        $value = current_script();
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MATCH_KEY_MATCH(string $lang, array $escaped, array $param) : string
{
    $value = '0';
    foreach ($param as $match_key) {
        if (($match_key == '1') || ($match_key == '0') || ($match_key == '')) {
            continue;
        }
        if (match_key_match($match_key, (!empty($param[1])))) {
            $value = '1';
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BLOCK(string $lang, array $escaped, array $param) : string
{
    if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) { // Normal operation
        $temp_array = [];
        handle_symbol_preprocessing([$escaped, TC_SYMBOL, 'BLOCK', $param], $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases

        foreach ($param as $i => $p) {
            if (is_object($p)) {
                $param[$i] = $p->evaluate();
            }
        }

        if ((count($param) == 1) && (strpos($param[0], ',') !== false)) { // NB: This code is also in Tempcode.php
            $param_2 = comma_list_str_to_arr($param[0], true);
        } else {
            $param_2 = $param;
        }

        foreach ($param_2 as &$_param) {
            $_param = preg_replace('#^\s*([^\s]+)\s*=#', '$1=', $_param);
        }

        if ((in_array('defer=1', $param_2)) && (!running_script('comcode_convert'))) {
            $value = static_evaluate_tempcode(do_template('JS_BLOCK', [
                '_GUID' => '2334719e23b2773ad04fe0fcbdce684d',
                'BLOCK_PARAMS' => comma_list_arr_to_str($param_2),
                'SELF_URL' => get_self_url(),
            ]));
        } else {
            global $BLOCKS_CACHE;
            if (isset($BLOCKS_CACHE[serialize($param_2)])) { // Will always be set
                $value = $BLOCKS_CACHE[serialize($param_2)]->evaluate();
            } else {
                $value = '';
                if ($GLOBALS['XSS_DETECT']) {
                    ocp_mark_as_escaped($value);
                }
            }
        }
    } else {
        $value = '';
        if ($GLOBALS['XSS_DETECT']) {
            ocp_mark_as_escaped($value);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FORCE_PREVIEWS(string $lang, array $escaped, array $param) : string
{
    if (get_option('forced_preview_option') == '1') {
        if (get_forum_type() == 'cns') {
            if ((is_guest()) && (get_option('default_preview_guests') == '0')) {
                $value = '0';
            } else {
                $value = ($GLOBALS['FORUM_DRIVER']->get_member_row_field(get_member(), 'm_preview_posts') == 1) ? '1' : '0';
            }
        } else {
            $value = (get_option('default_preview_guests') == '0') ? '0' : '1';
        }
    } else {
        $value = '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PREVIEW_URL(string $lang, array $escaped, array $param) : string
{
    $value = find_script('preview');
    $value .= '?page=' . urlencode(get_page_name());
    $value .= '&type=' . urlencode(get_param_string('type', '', INPUT_FILTER_GET_COMPLEX));
    $lang = get_param_string('lang', null);
    if ($lang !== null) {
        $value .= '&lang=' . urlencode($lang);
    }
    $keep = symbol_tempcode('KEEP');
    $value .= $keep->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HIDE_HELP_PANEL(string $lang, array $escaped, array $param) : string
{
    $value = (!empty($_COOKIE['hide_helper_panel'])) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FIND_SCRIPT_NOHTTP(string $lang, array $escaped, array $param) : string
{
    if ($GLOBALS['DEV_MODE']) { // Dev mode changes base domain so we need to actually use it in full (fine, we don't have HTTPS in dev mode).
        return ecv_FIND_SCRIPT($lang, $escaped, $param);
    }

    $value = preg_replace('#^https?://[^/]+#', '', ecv_FIND_SCRIPT($lang, [], $param));
    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FIND_SCRIPT(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (function_exists('find_script'))) {
        $value = find_script($param[0]);
    } else { // Running from installer's compilation of JS, so let's assume it's a simple case
        $value = get_base_url() . '/data/' . $param[0] . '.php';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CSS_TEMPCODE(string $lang, array $escaped, array $param) : string
{
    $temp_array = [];
    handle_symbol_preprocessing([$escaped, TC_SYMBOL, 'CSS_TEMPCODE', $param], $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases

    $_value = css_tempcode();
    $value = $_value->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_JS_TEMPCODE(string $lang, array $escaped, array $param) : string
{
    $temp_array = [];
    handle_symbol_preprocessing([$escaped, TC_SYMBOL, 'JS_TEMPCODE', $param], $temp_array); // Late preprocessing. Should not be needed in case of full screen output (as this was properly preprocessed), but is in other cases

    $_value = javascript_tempcode();
    $value = $_value->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PAGE_TITLE(string $lang, array $escaped, array $param) : string
{
    global $DISPLAYED_TITLE;
    $value = ($DISPLAYED_TITLE === null) ? '' : $DISPLAYED_TITLE->evaluate();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_EXTRA_HEAD(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ($GLOBALS['EXTRA_HEAD'] !== null) {
        $_value = $GLOBALS['EXTRA_HEAD'];
        $value = $_value->evaluate();
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_EXTRA_FOOT(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ($GLOBALS['EXTRA_FOOT'] !== null) {
        $_value = $GLOBALS['EXTRA_FOOT'];

        if (!@cms_empty_safe($param[0])) { // Set
            $GLOBALS['EXTRA_FOOT']->attach($param[0]);
        } else { // Get
            $value = $_value->evaluate();
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SHOW_DOCS(string $lang, array $escaped, array $param) : string
{
    $value = (get_option('show_docs') === '0') ? '0' : '1';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_INTEGER_FORMAT(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[0])) && (is_numeric($param[0]))) {
        $value = integer_format(intval($param[0]), (isset($param[1]) && is_numeric($param[1])) ? intval($param[1]) : null);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_RAND(string $lang, array $escaped, array $param) : string
{
    if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) { // Normal operation
        if ((isset($param[1])) && (is_numeric($param[1]))) {
            $min = intval($param[0]);
            $max = intval($param[1]);
        } elseif ((isset($param[0])) && (is_numeric($param[0]))) {
            $min = 0;
            $max = intval($param[0]);
        } else {
            $min = 0;
            $max = mt_getrandmax();
        }
        if ($min > $max) {
            $tmp = $min;
            $min = $max;
            $max = $tmp;
        }

        static $before = []; // Don't allow repeats
        $key = strval($min) . '_' . strval($max);
        if (!isset($before[$key])) {
            $before[$key] = [];
        }
        do {
            $random = mt_rand($min, $max);
        } while (isset($before[$key][$random]));
        if (count($before[$key]) < $max - $min) {
            $before[$key][$random] = true;
        } else { // Reset, so we get another set to randomise through
            $before[$key] = [];
        }

        $value = strval($random);
    } else { // Been told to behave statically
        $value = '4';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}


/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SET_RAND(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) { // Normal operation
            $value = $param[mt_rand(0, count($param) - 1)];
        } else { // Been told to behave statically
            $value = $param[0];
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MEMBER_EMAIL(string $lang, array $escaped, array $param) : string
{
    $value = $GLOBALS['FORUM_DRIVER']->get_member_email_address(((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : get_member());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_PRIVILEGE(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((!empty($param[0])) && (function_exists('has_privilege'))) {
        $member_id = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();

        $value = has_privilege($member_id, $param[0], isset($param[2]) ? $param[2] : null, empty($param[3]) ? null : explode('|', $param[3])) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_ACTUAL_PAGE_ACCESS(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!@cms_empty_safe($param[0])) {
        $value = (function_exists('get_member') && has_actual_page_access(((isset($param[2])) && (is_numeric($param[2]))) ? intval($param[2]) : get_member(), $param[0], isset($param[1]) ? $param[1] : null)) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_ZONE_ACCESS(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[0])) && (function_exists('has_zone_access'))) {
        $member_id = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();

        $value = has_zone_access($member_id, $param[0]) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_EDIT_PERMISSION(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        $range = cms_strtolower_ascii($param[0]);
        $owner = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();
        $member_id = ((isset($param[2])) && (is_numeric($param[2]))) ? intval($param[2]) : get_member();
        $cms_page = ((!@cms_empty_safe($param[3]))) ? $param[3] : get_page_name();
        if (isset($param[5])) {
            $value = has_edit_permission($range, $member_id, $owner, $cms_page, [$param[4], $param[5]]) ? '1' : '0';
        } else {
            $value = has_edit_permission($range, $member_id, $owner, $cms_page) ? '1' : '0';
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HAS_DELETE_PERMISSION(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        $range = cms_strtolower_ascii($param[0]);
        $owner = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();
        $member_id = ((isset($param[2])) && (is_numeric($param[2]))) ? intval($param[2]) : get_member();
        $cms_page = ((!@cms_empty_safe($param[3]))) ? $param[3] : get_page_name();
        if (isset($param[5])) {
            $value = has_delete_permission($range, $member_id, $owner, $cms_page, [$param[5], $param[6]]) ? '1' : '0';
        } else {
            $value = has_delete_permission($range, $member_id, $owner, $cms_page) ? '1' : '0';
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DATE_TIME(string $lang, array $escaped, array $param) : string
{
    $time = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : time();
    $use_contextual_dates = (!isset($param[1]) || ($param[1] == '1'));
    $utc_time = (!empty($param[2]));
    $member_id = ((isset($param[3])) && (is_numeric($param[3]))) ? intval($param[3]) : null;
    $value = get_timezoned_date_time($time, $use_contextual_dates, $utc_time, $member_id);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DATE(string $lang, array $escaped, array $param) : string
{
    $time = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : time();
    $use_contextual_dates = (!isset($param[1]) || ($param[1] == '1'));
    $utc_time = (!empty($param[2]));
    $member_id = ((isset($param[3])) && (is_numeric($param[3]))) ? intval($param[3]) : null;
    $value = get_timezoned_date($time, $use_contextual_dates, $utc_time, $member_id);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TIME(string $lang, array $escaped, array $param) : string
{
    $time = ((isset($param[0])) && (is_numeric($param[0]))) ? intval($param[0]) : time();
    $use_contextual_times = (!empty($param[1]));
    $utc_time = (!empty($param[2]));
    $member_id = ((isset($param[3])) && (is_numeric($param[3]))) ? intval($param[3]) : null;
    $value = get_timezoned_time($time, $use_contextual_times, $utc_time, $member_id);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FROM_TIMESTAMP(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        $timestamp = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : time();

        if ($GLOBALS['STATIC_TEMPLATE_TEST_MODE']) {
            require_code('lorem');
            $timestamp = intval(placeholder_date_raw());
        }

        if ((!isset($param[2])) || ($param[2] === '1')) {
            $timestamp = utctime_to_usertime($timestamp);
        }
        $value = cms_date($param[0], $timestamp);
    } else {
        if ($GLOBALS['STATIC_TEMPLATE_TEST_MODE']) {
            require_code('lorem');
            $value = placeholder_date_raw();
        } else {
            $timestamp = time();
            $value = strval($timestamp);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DEV_MODE(string $lang, array $escaped, array $param) : string
{
    $value = $GLOBALS['DEV_MODE'] ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BROWSER_MATCHES(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        $q = false;
        foreach (explode('|', $param[0]) as $browser) {
            $q = browser_matches($browser);
            if ($q) {
                break;
            }
        }
        $value = $q ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_INIT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[1])) {
        global $TEMPCODE_SETGET;
        if (!isset($TEMPCODE_SETGET[$param[0]])) {
            $TEMPCODE_SETGET[$param[0]] = $param[1];
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_INC(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[0])) {
        global $TEMPCODE_SETGET;
        if (!isset($TEMPCODE_SETGET[$param[0]])) {
            $TEMPCODE_SETGET[$param[0]] = '0';
        }

        if (!empty($param[1])) {
            $value = $TEMPCODE_SETGET[$param[0]];
        }

        $TEMPCODE_SETGET[$param[0]] = strval(intval($TEMPCODE_SETGET[$param[0]]) + 1);
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PREG_MATCH(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[1])) {
        $result = @preg_match('#' . str_replace('#', '\#', $param[0]) . '#' . (isset($param[2]) ? str_replace('e', '', $param[2]) : ''), $param[1]);
        if ($result === false) {
            attach_message(cms_error_get_last(), 'warn', false, true);
        } else {
            $value = ($result != 0) ? '1' : '0';
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PREG_REPLACE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[2])) {
        $value = @preg_replace('#' . str_replace('#', '\#', $param[0]) . '#' . (isset($param[3]) ? str_replace('e', '', $param[3]) : ''), $param[1], $param[2]);
        if ($value === false) {
            attach_message(cms_error_get_last(), 'warn', false, true);
            $value = '';
        }

        if ($GLOBALS['XSS_DETECT'] && ocp_is_escaped($param[0])) {
            ocp_mark_as_escaped($value);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SSW(string $lang, array $escaped, array $param) : string
{
    $value = (get_option('ssw') == '1') ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MAX(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && (is_numeric($param[0])) && (is_numeric($param[1]))) {
        $value = strval(max(intval($param[0]), intval($param[1])));
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MIN(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && (is_numeric($param[0])) && (is_numeric($param[1]))) {
        $value = strval(min(intval($param[0]), intval($param[1])));
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DIV_FLOAT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[0])) && (is_numeric($param[0])) && (!empty($param[1]))) {
        $value = float_to_raw_string(floatval($param[0]) / floatval($param[1]), 20, true);
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DIV(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[0])) && (is_numeric($param[0])) && (!empty($param[1]))) {
        $value = strval(intval(floor(floatval($param[0]) / floatval($param[1]))));
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_VALID_FILE_TYPES(string $lang, array $escaped, array $param) : string
{
    if (!function_exists('get_option')) {
        return '';
    }

    $value = get_option('valid_types');
    $types = array_flip(explode(',', $value));
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    cms_mb_ksort($types, SORT_NATURAL | SORT_FLAG_CASE);
    foreach (array_flip($types) as $val) {
        $value .= $val . ',';
    }
    $value = substr($value, 0, strlen($value) - 1);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_UCASE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        if (!empty($param[1])) {
            $value = cms_mb_strtoupper(cms_mb_substr($param[0], 0, 1)) . cms_mb_substr($param[0], 1); // ucfirst
        } else {
            $value = cms_mb_strtoupper($param[0]);
        }
    }

    if ($GLOBALS['XSS_DETECT'] && ocp_is_escaped($param[0])) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LCASE(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[0])) {
        $value = cms_mb_strtolower($param[0]);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    if ($GLOBALS['XSS_DETECT'] && ocp_is_escaped($param[0])) {
        ocp_mark_as_escaped($value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv__POST(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!@cms_empty_safe($param[0])) {
        $value = post_param_string($param[0], isset($param[1]) ? $param[1] : '');
    }

    if ((!has_solemnly_declared(I_UNDERSTAND_XSS)) && (empty($escaped))) {
        $escaped = [ENTITY_ESCAPED];
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_REPLACE(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[2])) {
        $value = str_replace($param[0], $param[1], $param[2]);
    }

    if ($GLOBALS['XSS_DETECT'] && ocp_is_escaped($param[0])) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IN_STR(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[1])) {
        if ($param[1] == '') { // Would generate a PHP notice
            $value = '0';
        } else {
            $value = '0';
            foreach ($param as $i => $check) {
                if ((is_integer($i)) && ($i != 0) && ($check != '')) {
                    if (strpos($param[0], $check) !== false) {
                        $value = '1';
                        break;
                    }
                }
            }
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SUBSTR_COUNT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[1])) {
        if ($param[0] == '') {
            $value = '0';
        } else {
            $value = strval(substr_count($param[0], $param[1]));
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SUBSTR(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[1])) && (is_numeric($param[1]))) {
        $value = cms_mb_substr($param[0], intval($param[1]), ((isset($param[2])) && (is_numeric($param[2]))) ? intval($param[2]) : strlen($param[0]));
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ALTERNATOR_TRUNCATED(string $lang, array $escaped, array $param) : string // Alternate values according to whether some given text WOULD have been truncated. 0: text to check against, 1: the truncate length, 2:IF would not be do this, 3: if it would be do this, 4: whether given text is encoded as HTML (0=no [default, plain-text], 1=yes)
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[1])) && (is_numeric($param[1])) && (isset($param[3]))) {
        $amount = intval($param[1]);
        $is_html = (!empty($param[4]));
        if (strlen($is_html ? strip_tags($param[0]) : $param[0]) > $amount) {
            $value = $param[3];
        } else {
            $value = $param[2];
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_EQ(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!isset($param[0])) {
        $param[0] = '';
    }
    if (!isset($param[1])) {
        $param[1] = '';
    }
    $first = array_shift($param);
    $count = 0;
    foreach ($param as $test) {
        if ($first == $test) {
            $count++;
            break;
        }
    }
    $value = ($count !== 0) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NEQ(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!isset($param[0])) {
        $param[0] = '';
    }
    if (!isset($param[1])) {
        $param[1] = '';
    }
    $first = array_shift($param);
    $count = 0;
    foreach ($param as $test) {
        if ($first == $test) {
            $count++;
        }
    }
    $value = ($count === 0) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NOT(string $lang, array $escaped, array $param) : string
{
    $value = '1';

    if (isset($param[0])) {
        $value = ($param[0] == '1') ? '0' : '1';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_OR(string $lang, array $escaped, array $param) : string
{
    $value = '';

    $count = 0;
    foreach ($param as $test) {
        if ($test == '1') {
            $count++;
        }
    }
    $value = ($count > 0) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_AND(string $lang, array $escaped, array $param) : string
{
    $count = 0;
    $total = 0;
    foreach ($param as $test) {
        if ($test === '1') {
            $count++;
        }
        $total++;
    }
    $value = ($count === $total) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NOR(string $lang, array $escaped, array $param) : string
{
    $count = 0;
    foreach ($param as $test) {
        if ($test === '1') {
            $count++;
        }
    }
    $value = ($count > 0) ? '0' : '1';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_NAND(string $lang, array $escaped, array $param) : string
{
    $count = 0;
    foreach ($param as $test) {
        if ($test === '1') {
            $count++;
        }
    }
    $value = ($count === count($param)) ? '0' : '1';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && (is_numeric($param[0])) && (is_numeric($param[1]))) {
        $value = (intval($param[0]) < intval($param[1])) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_GT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && (is_numeric($param[0])) && (is_numeric($param[1]))) {
        $value = (intval($param[0]) > intval($param[1])) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LENGTH(string $lang, array $escaped, array $param) : string
{
    $value = '0';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        $value = strval(cms_mb_strlen($param[0]));
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_INSERT_FORM_POST_SECURITY(string $lang, array $escaped, array $param) : string
{
    static $done_once = false;

    $value = '';

    if (!$GLOBALS['STATIC_TEMPLATE_TEST_MODE']) {
        if (get_option('spam_blackhole_detection') == '1' && !$done_once && get_page_name() != 'members'/*in case of some weird autocomplete issue when changing your password*/) {
            $field_name = 'y' . md5(get_site_name() . ': antispam');
            $tag = ((!empty($param[0])) ) ? 'span' : 'div';
            $value .= '<' . $tag . ' id="' . escape_html($field_name) . '-wrap" style="display:none" ' . ($GLOBALS['SEMI_DEV_MODE'] ? '' : 'data-remove-if-js-enabled="1"') . '>' .
                '<label for="' . escape_html($field_name) . '">' . do_lang('DO_NOT_FILL_ME_SPAMMER_BLACKHOLE') . '</label><input id="' . escape_html($field_name) . '" name="' . escape_html($field_name) . '" value="" type="text" /></' . $tag . '>';
            $done_once = true;
        }

        require_code('csrf_filter');
        $value .= '<input type="hidden" name="csrf_token" value="' . escape_html(generate_csrf_token()) . '" />';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HONEYPOT_LINK(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    $honeypot_url = get_option('honeypot_url');
    if (($honeypot_url != '') && ($honeypot_url !== null)) {
        $first_char = substr(md5(get_page_name()), 0, 1);
        $bot_phrase = get_option('honeypot_phrase');
        switch ($first_char) {
            case '0':
            case '1':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '"><' . '!-- ' . escape_html($bot_phrase) . ' --></a>';
                break;
            case '2':
            case '3':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '"><img alt="' . escape_html($bot_phrase) . '" src="' . escape_html(find_theme_image('blank')) . '" height="1" width="1" border="0" /></a>';
                break;
            case '4':
            case '5':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '" style="display: none;">' . escape_html($bot_phrase) . '</a>';
                break;
            case '6':
            case '7':
                $value = '<div style="display: none;"><a rel="nofollow" href="' . escape_html($honeypot_url) . '">' . escape_html($bot_phrase) . '</a></div>';
                break;
            case '8':
            case '9':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '"></a>';
                break;
            case 'a':
            case 'b':
                $value = '<' . '!-- <a rel="nofollow" href="' . escape_html($honeypot_url) . '">' . escape_html($bot_phrase) . '</a> -->';
                break;
            case 'c':
            case 'd':
                $value = '<div style="position: absolute; top: -250px; left: -250px;"><a rel="nofollow" href="' . escape_html($honeypot_url) . '">' . escape_html($bot_phrase) . '</a></div>';
                break;
            case 'e':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '"><span style="display: none;">' . escape_html($bot_phrase) . '</span></a>';
                break;
            case 'f':
                $value = '<a rel="nofollow" href="' . escape_html($honeypot_url) . '"><div style="height: 0px; width: 0px;"></div></a>';
                break;
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_COMMENT_COUNT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ($GLOBALS['STATIC_TEMPLATE_TEST_MODE']) {
        $value = '1';
    } else {
        if ((!empty($param[0])) && (isset($param[1]))) {
            static $cache_comment_count = [];
            $cache_key = $param[0] . '_' . $param[1];
            if (isset($cache_comment_count[$cache_key])) {
                $value = $cache_comment_count[$cache_key];
            } else {
                if (get_option('is_on_comments') == '1') {
                    $count = 0;
                    $topic_id = $GLOBALS['FORUM_DRIVER']->find_topic_id_for_topic_identifier(get_option('comments_forum_name'), $cache_key, do_lang('COMMENT'));
                    if ($topic_id !== null) {
                        $_comments = $GLOBALS['FORUM_DRIVER']->get_forum_topic_posts($topic_id, $count, 0, 0, false);
                        if (($count != 0) || (empty($param[2]))) {
                            if (is_array($_comments)) {
                                $_value = do_lang_tempcode('_COMMENTS', escape_html(integer_format($count, 0)));
                            } else {
                                $_value = do_lang_tempcode('_COMMENTS', escape_html(integer_format(0, 0)));
                            }
                            $value = $_value->evaluate();
                        }
                    } else {
                        $_value = do_lang_tempcode('_COMMENTS', escape_html(integer_format(0, 0)));
                        $value = $_value->evaluate();
                    }
                } else {
                    $value = do_lang('VIEW');
                }
                $cache_comment_count[$cache_key] = $value;
            }
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SELF_URL(string $lang, array $escaped, array $param) : string
{
    $extra_params = [];
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[3])) {
        $i = 3;
        while (isset($param[$i])) {
            $bits = explode('=', $param[$i], 2);
            if (count($bits) == 2) {
                if ($bits[1] == '<null>') {
                    $bits[1] = null;
                }
                $extra_params[$bits[0]] = $bits[1];
            }
            $i++;
        }
    }
    $root_if_posted = (!empty($param[0]));
    $posted_too = (!empty($param[1]));
    $avoid_remap = (!empty($param[2]));
    $value = get_self_url(true, $root_if_posted, $extra_params, $posted_too, $avoid_remap);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_REVIEW_STATUS(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (isset($param[1]))) {
        if (addon_installed('content_reviews')) {
            require_code('content_reviews');
            $value = show_content_reviews($param[0], $param[1], (!empty($param[2])) ? intval($param[2]) : 0);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv__POSTED(string $lang, array $escaped, array $param) : string
{
    return ($_SERVER['REQUEST_METHOD'] == 'POST') ? '1' : '0';
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_EXTEND_URL(string $lang, array $escaped, array $param) : string
{
    $value = isset($param[0]) ? $param[0] : '';
    $append = isset($param[1]) ? $param[1] : '';
    extend_url($value, $append);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_MOBILE(string $lang, array $escaped, array $param) : string
{
    $value = is_mobile(null, !empty($param[0]), !empty($param[1])) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DESKTOP(string $lang, array $escaped, array $param) : string
{
    $value = !is_mobile(null, !empty($param[0]), !empty($param[1])) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ENSURE_PROTOCOL_SUITABILITY(string $lang, array $escaped, array $param) : string
{
    $value = ensure_protocol_suitability(isset($param[0]) ? $param[0] : '');

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}


/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LANG(string $lang, array $escaped, array $param) : string
{
    $value = user_lang();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_THEME(string $lang, array $escaped, array $param) : string
{
    if (isset($GLOBALS['FORUM_DRIVER'])) {
        $value = $GLOBALS['FORUM_DRIVER']->get_theme();
    } else {
        $value = 'default';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_AUTHOR_MEMBER(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (!empty($param[0])) {
        if (addon_installed('authors')) {
            require_code('authors');
            $member_id = get_author_id_from_name($param[0]);
            if ($member_id === null) {
                $member_id = $GLOBALS['FORUM_DRIVER']->get_guest_id();
            }
            $value = strval($member_id);
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BRAND_NAME(string $lang, array $escaped, array $param) : string
{
    $value = brand_name();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BRAND_BASE_URL(string $lang, array $escaped, array $param) : string
{
    $value = get_brand_base_url();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SITE_NAME(string $lang, array $escaped, array $param) : string
{
    $value = get_site_name();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TIMEZONE(string $lang, array $escaped, array $param) : string
{
    require_code('temporal2');
    $value = make_nice_timezone_name(get_site_timezone());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CHARSET(string $lang, array $escaped, array $param) : string
{
    $value = get_charset();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LTR(string $lang, array $escaped, array $param) : string
{
    $value = (do_lang('dir') == 'ltr') ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_RTL(string $lang, array $escaped, array $param) : string
{
    $value = (do_lang('dir') == 'rtl') ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_COPYRIGHT(string $lang, array $escaped, array $param) : string
{
    $value = get_option('copyright');
    if (strpos($value, '$') !== false) {
        $value = str_replace('$CURRENT_YEAR=', '', $value); // Update-on-posting, does nothing dynamically
        $value = str_replace('$CURRENT_YEAR', date('Y'), $value); // Always updated
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}


/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CUSTOM_BASE_URL(string $lang, array $escaped, array $param) : string
{
    $value = get_custom_base_url();

    if (!empty($param[1])) {
        $value = cdn_filter($value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BASE_URL_NOHTTP(string $lang, array $escaped, array $param) : string
{
    if ($GLOBALS['DEV_MODE']) { // Dev mode changes base domain so we need to actually use it in full (fine, we don't have HTTPS in dev mode).
        return ecv_BASE_URL($lang, $escaped, $param);
    }

    $value = preg_replace('#^https?://[^/]+#', '', get_base_url());
    if (substr($value, 0, 2) == '//') {
        $value = substr($value, 1);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CUSTOM_BASE_URL_NOHTTP(string $lang, array $escaped, array $param) : string
{
    if ($GLOBALS['DEV_MODE']) { // Dev mode changes base domain so we need to actually use it in full (fine, we don't have HTTPS in dev mode).
        return ecv_BASE_URL($lang, $escaped, $param);
    }

    $value = preg_replace('#^https?://[^/]+/#', '/', get_custom_base_url());
    if (substr($value, 0, 2) == '//') {
        $value = substr($value, 1);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BASE_URL(string $lang, array $escaped, array $param) : string
{
    $value = get_base_url();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_INLINE_STATS(string $lang, array $escaped, array $param) : string
{
    $value = (get_option('show_inline_stats') == '1') ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_A_COOKIE_LOGIN(string $lang, array $escaped, array $param) : string
{
    global $IS_A_COOKIE_LOGIN;
    $value = ($IS_A_COOKIE_LOGIN && (ini_get('suhosin.cookie.max_name_length') !== '64')) ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_COOKIE_PATH(string $lang, array $escaped, array $param) : string
{
    $value = function_exists('get_cookie_path') ? get_cookie_path() : '/';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_COOKIE_DOMAIN(string $lang, array $escaped, array $param) : string
{
    $s_value = function_exists('get_cookie_domain') ? get_cookie_domain() : '';
    $value = ($s_value === null) ? '' : $s_value;

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SESSION_COOKIE_NAME(string $lang, array $escaped, array $param) : string
{
    $value = function_exists('get_session_cookie') ? get_session_cookie() : '';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BROWSER_UA(string $lang, array $escaped, array $param) : string
{
    $browser = get_browser_string();
    $value = $browser;

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DOMAIN(string $lang, array $escaped, array $param) : string
{
    $value = get_domain();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FORUM_BASE_URL(string $lang, array $escaped, array $param) : string
{
    $value = get_forum_base_url();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_GROUP_ID(string $lang, array $escaped, array $param) : string
{
    $value = '';

    $i = (!empty($param[0])) ? intval($param[0]) : 0;
    $member_id = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : get_member();
    $groups = $GLOBALS['FORUM_DRIVER']->get_members_groups($member_id);
    $value = isset($groups[$i]) ? strval($groups[$i]) : '';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_HTTP_STATUS_CODE(string $lang, array $escaped, array $param) : string
{
    global $HTTP_STATUS_CODE;
    $value = strval($HTTP_STATUS_CODE);

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_OS(string $lang, array $escaped, array $param) : string
{
    $os = get_os_string();
    if ($os === null) {
        $os = '';
    }
    $value = $os;

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_USER_AGENT(string $lang, array $escaped, array $param) : string
{
    $value = $_SERVER['HTTP_USER_AGENT'];

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IP_ADDRESS(string $lang, array $escaped, array $param) : string
{
    $value = get_ip_address();

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_VERSION(string $lang, array $escaped, array $param) : string
{
    $value = strval(cms_version());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_VERSION_NUMBER(string $lang, array $escaped, array $param) : string
{
    if (!empty($param[0])) {
        require_code('version2');
        $value = get_version_dotted();
    } else {
        $value = cms_version_pretty();
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_JSON_ENCODE(string $lang, array $escaped, array $param) : string
{
    $value = json_encode($param[0], JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CONFIG_OPTION(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        if ($GLOBALS['IN_MINIKERNEL_VERSION']) { // Installer, likely executing global.js. We need a saner default for JavaScript
            $value = '0';
        } else {
            $value = get_option($param[0], !empty($param[1]));
            if ($value === null) {
                $value = '';
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_VALUE_OPTION(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $value = function_exists('get_value') ? get_value($param[0]) : '';
        if ($value === null) {
            $value = function_exists('get_value') ? get_value($param[0], null, true) : '';
            if ($value === null) {
                $value = isset($param[1]) ? $param[1] : '';
                if (($param[0] == 'textmate') && ((is_local_machine()) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Macintosh') !== false))) {
                    $value = '1';
                }
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_THEME_OPTION(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        if ($GLOBALS['IN_MINIKERNEL_VERSION']) { // Installer, likely executing global.js. We need a saner default for JavaScript
            switch ($param[0]) {
                case 'font':
                    $value = 'Arial';
                    break;
                case 'font_size':
                    $value = '15';
                    break;
                default:
                    $value = '0';
                    break;
            }
        } else {
            $value = get_theme_option($param[0], isset($param[2]) ? $param[2] : null, null, !empty($param[1]));
            if ($value === null) {
                $value = '';
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ECOMMERCE_OPTION(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (addon_installed('ecommerce'))) {
        if ($GLOBALS['IN_MINIKERNEL_VERSION']) { // Installer, likely executing global.js. We need a saner default for JavaScript
            $value = '0';
        } else {
            require_code('ecommerce');

            $value = get_ecommerce_option($param[0], !empty($param[1]));
            if ($value === null) {
                $value = '';
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ADDON_INSTALLED(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((!empty($param[0])) && (!running_script('install'))) {
        $value = (addon_installed(trim($param[0]), !empty($param[1]))) ? '1' : '0';
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CNS(string $lang, array $escaped, array $param) : string
{
    $value = (get_forum_type() == 'cns') ? '1' : '0';

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_STRIP_HTML(string $lang, array $escaped, array $param) : string
{
    $value = strip_html($param[0]);

    if ($GLOBALS['XSS_DETECT'] && ocp_is_escaped($param[0])) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_LOGIN_LABEL(string $lang, array $escaped, array $param) : string
{
    switch (get_option('one_per_email_address')) {
        case '1':
            $value = '<span class="must-show-together">' . do_lang('USERNAME') . ' / ' . do_lang('EMAIL_ADDRESS') . '</span>';
            break;

        case '2':
            $value = do_lang('EMAIL_ADDRESS');
            break;

        default:
        case '0':
            $value = do_lang('USERNAME');
            break;
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TUTORIAL_URL(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $value = get_tutorial_url($param[0]);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CSP_NONCE(string $lang, array $escaped, array $param) : string
{
    global $CSP_NONCE;
    $value = isset($CSP_NONCE) ? $CSP_NONCE : '';
    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_CSP_NONCE_HTML(string $lang, array $escaped, array $param) : string
{
    if (function_exists('csp_nonce_html')) {
        $value = csp_nonce_html();
    } else {
        $value = '';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FONTS(string $lang, array $escaped, array $param) : string
{
    $value = '';

    $fonts = array_map('trim', explode(',', get_option('editor_fonts')));
    $google_fonts = array_map('trim', explode(',', get_option('google_fonts')));
    $all_fonts = array_unique(array_merge($fonts, $google_fonts));
    cms_mb_sort($all_fonts, SORT_NATURAL | SORT_FLAG_CASE);
    foreach ($all_fonts as $font) {
        if ($font != '') {
            $value .= ',';
        }
        $value .= $font;
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PROTECT_URL_PARAMETER(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (isset($param[0])) {
        $value = _protect_url_parameter($param[0]);
        if ($value === null) {
            $value = '';
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}


/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_PUBLIC_CONFIG_OPTIONS_JSON(string $lang, array $escaped, array $param) : string
{
    $_value = [];
    $hooks = find_all_hook_obs('systems', 'config', 'Hook_config_');

    foreach ($hooks as $hook => $ob) {
        $details = $ob->get_details();

        if (!empty($details['public'])) {
            if ($GLOBALS['IN_MINIKERNEL_VERSION']) { // Installer, likely executing global.js. We need a saner default for JavaScript
                $_value[$hook] = '0';
            } elseif (!empty($details['theme_override'])) {
                $_value[$hook] = get_theme_option($hook);
            } elseif (!empty($details['ecommerce'])) {
                $_value[$hook] = get_ecommerce_option($hook);
            } else {
                $_value[$hook] = get_option($hook);
            }
        }
    }

    $value = json_encode($_value, JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SMART_LINK_STRIP(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[1])) && (is_numeric($param[1]))) {
        $value = $param[0];

        if ((!has_no_forum()) && ($GLOBALS['FORUM_DRIVER']->get_post_count(intval($param[1])) < 1)) {
            $value = cms_strip_tags($value, '<a>', false);

            if ($value != $param[0]) {
                $value .= '<br /><em>' . do_lang('LINKS_STRIPPED') . '</em>';
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Find URL patterns staff tooltips can be added on.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_STAFF_TOOLTIPS_URL_PATTERNS_JSON(string $lang, array $escaped, array $param) : string
{
    $_url_patterns = '{}';

    if (!$GLOBALS['IN_MINIKERNEL_VERSION']) {
        require_code('content');

        $url_patterns = [];
        $cma_hooks = find_all_hooks('systems', 'content_meta_aware');
        foreach (array_keys($cma_hooks) as $content_type) {
            $content_type_ob = get_content_object($content_type);

            if (!isset($content_type_ob)) {
                continue;
            }

            $info = $content_type_ob->info();
            if (isset($info['view_page_link_pattern'])) {
                $url = page_link_to_tempcode_url($info['view_page_link_pattern'], true);
                $pattern = _escape_url_pattern_for_js_regex($url->evaluate());
                $hook = $content_type;
                $url_patterns[$pattern] = $hook;
            }
            if (isset($info['edit_page_link_pattern'])) {
                $url = page_link_to_tempcode_url($info['edit_page_link_pattern'], true);
                $pattern = _escape_url_pattern_for_js_regex($url->evaluate());
                $hook = $content_type;
                $url_patterns[$pattern] = $hook;
            }
        }

        $_url_patterns = json_encode($url_patterns, JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $_url_patterns);
    }

    return $_url_patterns;
}

/**
 * Convert a page-link pattern into a valid JS regexp.
 *
 * @param  string $pattern Pattern
 * @return string
 */
function _escape_url_pattern_for_js_regex(string $pattern) : string
{
    $pattern = '^' . $pattern;
    $pattern = str_replace('/', '\\/', $pattern);
    $pattern = str_replace('?', '\\?', $pattern);
    $pattern = str_replace('_WILD\\/', '([^&]*)\\/?', $pattern);
    $pattern = str_replace('_WILD', '([^&]*)', $pattern);

    return $pattern;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_BACKSLASH(string $lang, array $escaped, array $param) : string
{
    $value = '\\';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DECIMAL_POINT(string $lang, array $escaped, array $param) : string
{
    $value = do_lang('locale_decimal_point');

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_IS_ICON_IN_SVG_SPRITE(string $lang, array $escaped, array $param) : string
{
    if ((!function_exists('get_theme_option')) || (get_theme_option('sprite_icons') == '0')) {
        $value = '0';
        if ($GLOBALS['XSS_DETECT']) {
            ocp_mark_as_escaped($value);
        }
        return $value;
    }

    static $sprite_ids = null;

    if ($sprite_ids === null) {
        require_code('themes');
        $theme = isset($GLOBALS['FORUM_DRIVER']) ? $GLOBALS['FORUM_DRIVER']->get_theme() : 'default';
        $is_monochrome = function_exists('get_theme_option') && (get_theme_option('use_monochrome_icons') === '1');
        $sprite = '';

        $path = get_file_base() . '/themes/' . $theme . '/images_custom/icons' . ($is_monochrome ? '_monochrome' : '') . '_sprite.svg';
        if (!file_exists($path)) {
            $path = get_file_base() . '/themes/default/images_custom/icons' . ($is_monochrome ? '_monochrome' : '') . '_sprite.svg';
        }
        if (!file_exists($path)) {
            $path = get_file_base() . '/themes/default/images/icons' . ($is_monochrome ? '_monochrome' : '') . '_sprite.svg';
        }

        if (file_exists($path)) {
            $sprite = cms_file_get_contents_safe($path, FILE_READ_LOCK);
        }

        $sprite_ids = [];
        $matches = [];
        $num_matches = preg_match_all('#\sid="icon_([^"]*)"#', $sprite, $matches);
        for ($i = 0; $i < $num_matches; $i++) {
            $sprite_ids[$matches[1][$i]] = true;
        }
    }

    $icon_name = $param[0];
    $icon_id = str_replace('/', '__', $icon_name);

    $value = isset($sprite_ids[$icon_id]) ? '1' : '0';

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them
 * @return string The result
 */
function ecv_ATTR_DEFAULTED(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && ($param[1] != (isset($param[2]) ? $param[2] : ''))) {
        $value = ' ' . $param[0] . '="' . $param[1] . '"';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_FAVICON(string $lang, array $escaped, array $param) : string
{
    $default = find_theme_image('favicon');
    $value = $default;

    if (addon_installed('health_check')) {
        if (get_option('hc_is_test_site') == '1') {
            $value = find_theme_image(is_local_machine() ? 'favicon_dev' : 'favicon_staging', true);
            if ($value == '') {
                $value = $default;
            }
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them
 * @return string The result
 */
function ecv_TRANSLATION_LINKS(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!isset($_SERVER['REQUEST_METHOD']) || ($_SERVER['REQUEST_METHOD'] !== 'POST')) {
        $langs = find_all_langs();
        $alt_langs = [];
        foreach (array_keys($langs) as $lang) {
            $alt_langs[] = [
                'LANG' => $lang,
                'CONSISTENT_DEFAULT' => (get_site_default_lang() == $lang) && ((get_option('detect_lang_forum') == '0') || (is_guest())) && (get_option('detect_lang_browser') == '0'),
            ];
        }

        if (!empty($alt_langs)) {
            $_value = do_template('TRANSLATION_LINKS', ['_GUID' => '44b1d8c3a82878f365cb1a0b750d935b', 'ALT_LANGS' => $alt_langs]);
            $value = $_value->evaluate();
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SESSION(string $lang, array $escaped, array $param) : string
{
    $value = get_session_id();

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_SESSION_HASHED(string $lang, array $escaped, array $param) : string
{
    $value = md5(get_session_id());

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string
 */
function ecv_COLOR_RGBA(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if (!empty($param[0])) {
        $hex_color = $param[0];
        $red   = hexdec(substr($hex_color, 1, 2));
        $green = hexdec(substr($hex_color, 3, 2));
        $blue  = hexdec(substr($hex_color, 5, 2));
        $alpha = ((isset($param[1])) && (is_numeric($param[1]))) ? floatval($param[1]) : '1.0';

        $value = 'rgba(' . strval($red) . ', ' . strval($green) . ', ' . strval($blue) . ', ' . float_to_raw_string($alpha) . ')';
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string
 */
function ecv_COLOR_LIGHTEN(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (isset($param[1]))) {
        require_code('themes');

        $hex_color = $param[0];
        $red   = hexdec(substr($hex_color, 1, 2));
        $green = hexdec(substr($hex_color, 3, 2));
        $blue  = hexdec(substr($hex_color, 5, 2));

        $lighten_by = floatval(substr($param[1], 0, -1)) / 100.0; // Remove trailing '%' sign

        list($h, $s, $l) = rgb_to_hsl($red, $green, $blue);

        $l = min(1.0, max(0.0, ($l + $lighten_by)));

        list($red, $green, $blue) = hsl_to_rgb($h, $s, $l);

        $value = sprintf("#%02x%02x%02x", $red, $green, $blue);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string
 */
function ecv_COLOR_DARKEN(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (isset($param[1]))) {
        require_code('themes');

        $hex_color = $param[0];
        $red   = hexdec(substr($hex_color, 1, 2));
        $green = hexdec(substr($hex_color, 3, 2));
        $blue  = hexdec(substr($hex_color, 5, 2));

        $darken_by = floatval(substr($param[1], 0, -1)) / 100.0; // Remove trailing '%' sign

        list($h, $s, $l) = rgb_to_hsl($red, $green, $blue);

        $l = min(1.0, max(0.0, ($l - $darken_by)));

        list($red, $green, $blue) = hsl_to_rgb($h, $s, $l);

        $value = sprintf("#%02x%02x%02x", $red, $green, $blue);
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }

    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ADD(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if ((isset($param[1])) && (is_numeric($param[0])) && (is_numeric($param[1]))) {
        $_value = 0.0;

        foreach ($param as $p) {
            $_value += floatval(str_replace(',', '', $p));
        }

        $value = float_to_raw_string($_value, 20, true);
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_METADATA_IMAGE_EXTRACT(string $lang, array $escaped, array $param) : string
{
    $value = '';

    if (isset($param[0])) {
        global $METADATA;

        if (!empty($param[1])) { // Take priority?
            unset($METADATA['image']);
        }

        // Also scan for <img> tag, in case it was put in manually
        if ((empty($METADATA['image'])) && (stripos($param[0], '<img') !== false)) {
            $matches = [];
            if (preg_match('#<img\s[^<>]*src="([^"]*)"#i', $param[0], $matches) != 0) {
                set_extra_request_metadata([
                    'image' => html_entity_decode($matches[1], ENT_QUOTES),
                ]);
            }
        }
    }

    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_RATING(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((!empty($param[0])) && (!@cms_empty_safe($param[1]))) {
        static $cache_rating = [];
        $cache_key = serialize($param);
        if (isset($cache_rating[$cache_key])) {
            $value = $cache_rating[$cache_key];
        } else {
            global $DISPLAYED_TITLE;

            require_code('feedback');
            $display_tpl = (!empty($param[5])) ? 'RATING_FORM' : $param[5];
            $rating = get_rating_simple_array(
                empty($param[3]) ? get_self_url(true) : $param[3], // content_url
                @cms_empty_safe($param[4]) ? (($DISPLAYED_TITLE === null) ? '' : $DISPLAYED_TITLE->evaluate()) : $param[4], // content_title
                $param[0], // content_type
                $param[1], // content_id
                'RATING_FORM', // form_tpl
                ((isset($param[2])) && (is_numeric($param[2]))) ? intval($param[2]) : null // submitter
            );
            if ($rating !== null) {
                if (empty($param[5])) {
                    $value = isset($rating['ALL_RATING_CRITERIA'][key($rating['ALL_RATING_CRITERIA'])]['RATING']) ? $rating['ALL_RATING_CRITERIA'][key($rating['ALL_RATING_CRITERIA'])]['RATING'] : '';
                } else {
                    $value = static_evaluate_tempcode(do_template($display_tpl, $rating));
                }
            }

            $cache_rating[$cache_key] = $value;
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_ROUND(string $lang, array $escaped, array $param) : string
{
    $value = '';
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }

    if ((isset($param[0])) && (is_numeric($param[0]))) {
        $amount = ((isset($param[1])) && (is_numeric($param[1]))) ? intval($param[1]) : 0;
        if ($amount > 0) {
            $value = float_format(floatval($param[0]), $amount);
        } else {
            $value = strval(intval(round(floatval($param[0]), $amount)));
        }
    }

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_TAPATALK(string $lang, array $escaped, array $param) : string
{
    $value = (defined('IN_MOBIQUO') ? '1' : '0');

    if (!empty($escaped)) {
        apply_tempcode_escaping($escaped, $value);
    }
    return $value;
}

/**
 * Evaluate a particular Tempcode symbol.
 *
 * @ignore
 *
 * @param  LANGUAGE_NAME $lang The language to evaluate this symbol in (some symbols refer to language elements)
 * @param  array $escaped Array of escaping operations
 * @param  array $param Parameters to the symbol. For all but directive it is an array of strings. For directives it is an array of Tempcode objects. Actually there may be template-style parameters in here, as an influence of singular_bind and these may be Tempcode, but we ignore them.
 * @return string The result
 */
function ecv_DB_FIRST_ID(string $lang, array $escaped, array $param) : string
{
    $value = strval(db_get_first_id());
    if ($GLOBALS['XSS_DETECT']) {
        ocp_mark_as_escaped($value);
    }
    return $value;
}
