<?php

include_once __DIR__.'/../vendor/autoload.php';

use \CB\Core;

// Массив данных с "русскими" именами полей
function data_table($name_table, $where = "", $return_rows = "1")
{
    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    if ($table_id) {
        $table = get_table($table_id);
        $table_fields = get_table_fields($table);

        // формируем набор полей и заменяем имена в условии
        foreach ($table_fields as $one_field) {
            $fields[$one_field['int_name']] = $one_field['name_field'];
            $where = str_replace("`" . $one_field['name_field'] . "`", $one_field['int_name'], $where);
        }

        // получаем строку данных из таблицы и заменяем внутрение имена полей на внешние
        $result = data_select($table_id,
            ($where ? $where : "1=1") . (($return_rows != "all") ? " LIMIT $return_rows" : ""));
        while ($row = sql_fetch_array($result)) {
            foreach ($row as $int_name => $value) {
                if ($fields[$int_name]) {
                    $data[$fields[$int_name]] = $value;
                }
            }
            $lines[] = $data;
        }

        // возвращаем результат
        if ($return_rows == 1) {
            if ($lines) {
                return $lines[0];
            } else {
                return false;
            }
        } else {
            if ($lines) {
                return $lines;
            } else {
                return array();
            }
        }
    } else {
        echo "Error in function 'data_table': table '" . $name_table . "' no found.";
        return false;
    }
}

// Вставка данных в таблицу name_table массивом data с "русскими" именами полей
function insert_query($data, $name_table)
{
    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    if ($table_id) {
        $table = get_table($table_id);
        $table_fields = get_table_fields($table);

        // формируем набор для вставки
        foreach ($table_fields as $one_field) {
            if (isset($data[$one_field['name_field']])) {
                $fields[$one_field['int_name']] = $data[$one_field['name_field']];
            }
        }

        // вставляем данные
        $new_id = data_insert($table_id, EVENTS_ENABLE, $fields);

        // возвращаем id новой записи
        return $new_id;
    } else {
        echo "Error in function 'insert_query': table '" . $name_table . "' no found.";
        return false;
    }
}

// Обновление данных таблицы name_table массивом data с "русскими" именами полей
function update_query($data, $name_table, $where = "")
{
    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    if ($table_id) {
        $table = get_table($table_id);
        $table_fields = get_table_fields($table);

        // формируем набор для обновления и заменяем имена в условии
        foreach ($table_fields as $one_field) {
            if (isset($data[$one_field['name_field']])) {
                $fields[$one_field['int_name']] = $data[$one_field['name_field']];
            }
            $where = str_replace("`" . $one_field['name_field'] . "`", $one_field['int_name'], $where);
        }

        // обновляем таблицу
        data_update($table_id, EVENTS_ENABLE, $fields, $where ? $where : "1=1");
    } else {
        echo "Error in function 'update_query': table '" . $name_table . "' no found.";
    }
}

// Удаление строки таблицы name_table
function delete_query($name_table, $where = "")
{
    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    if ($table_id) {
        $table = get_table($table_id);
        $table_fields = get_table_fields($table);

        // заменяем внешние имена в условии на внутренние
        foreach ($table_fields as $one_field) {
            $where = str_replace("`" . $one_field['name_field'] . "`", $one_field['int_name'], $where);
        }

        // обновляем таблицу
        data_update($table_id, EVENTS_ENABLE, array('status' => 2), $where ? $where : "1=1");
    } else {
        echo "Error in function 'delete_query': table '" . $name_table . "' no found.";
    }
}

// Совместимость со старой функцией update_table
function update_table($data, $name_table, $where = "")
{
    return update_query($data, $name_table, $where);
}

// Поиск по полю
function set_search($name_table, $name_field, $compare, $value, $value2 = "", $union = " and ")
{
    global $ses_id;

    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    // получаем id поля по его имени
    $field = sql_select_array(FIELDS_TABLE, "table_id=", $table_id, " AND name_field='", $name_field, "'");
    $field_id = $field['id'];

    $condition['field'] = $field_id;
    $condition['term'] = $compare;
    $condition['value'] = $value;
    $condition['value2'] = $value2;
    $condition['union'] = $union;

    $i = count($_SESSION[$ses_id]['search'][$table_id]) + 1;
    $_SESSION[$ses_id]['search'][$table_id][$i] = $condition;
}

// Сброс поиска по таблице/полю
function reset_search($name_table, $name_field = "")
{
    global $ses_id;

    // получаем id таблицы по ее имени
    $table = sql_select_array(TABLES_TABLE, "name_table='", $name_table, "'");
    $table_id = $table['id'];

    if ($name_field) {
        // получаем id поля по его имени
        $field = sql_select_array(FIELDS_TABLE, "table_id=", $table_id, " AND name_field='", $name_field, "'");
        $field_id = $field['id'];

        $set_cond_old = is_array($_SESSION[$ses_id]['search'][$table_id]) ? $_SESSION[$ses_id]['search'][$table_id] : array();
        $i = 1;
        foreach ($set_cond_old as $condition) {
            if ($condition['field'] != $field_id) {
                $set_cond[$i++] = $condition;
            }
        }
        $_SESSION[$ses_id]['search'][$table_id] = $set_cond;
    } else {
        unset($_SESSION[$ses_id]['search'][$table_id]);
    }
}

// Установка фильтра по полю
function set_filter($field_id, $compare, $value, $value2 = "", $union = " and ")
{
    global $ses_id;

    $field = sql_select_array(FIELDS_TABLE, "id=", $field_id);
    $table_id = $field['table_id'];

    $condition['field'] = $field_id;
    $condition['term'] = $compare;
    $condition['value'] = $value;
    $condition['value2'] = $value2;
    $condition['union'] = $union;

    $i = is_array($_SESSION[$ses_id]['search'][$table_id]) ? count($_SESSION[$ses_id]['search'][$table_id]) + 1 : 1;
    $_SESSION[$ses_id]['search'][$table_id][$i] = $condition;
    if ($_SESSION[$ses_id]['template_id'][$table_id]) {
        $_SESSION[$ses_id]['template_id'][$table_id] = '';
    }
}

// Сброс фильтра по полю
function reset_filter($field_id)
{
    global $ses_id;

    $field = sql_select_array(FIELDS_TABLE, "id=", $field_id);
    $table_id = $field['table_id'];

    $set_cond_old = is_array($_SESSION[$ses_id]['search'][$table_id]) ? $_SESSION[$ses_id]['search'][$table_id] : array();
    $i = 1;
    foreach ($set_cond_old as $condition) {
        if ($condition['field'] != $field_id) {
            $set_cond[$i++] = $condition;
        }
    }
    $_SESSION[$ses_id]['search'][$table_id] = $set_cond;
}

// Сброс фильтров по таблице
function reset_filters($table_id)
{
    global $ses_id;

    unset($_SESSION[$ses_id]['search'][$table_id]);
}

// выбор шаблона с учетом варианта дизайна
function smarty_display($tpl_name)
{
    global $config, $smarty, $new_design_on;

    $tpl_name = $tpl_name . '.tpl';
    $path = $config['site_path'] . '/templates/' . $tpl_name;
    $m_path = $config['site_path'] . '/mobile/templates/' . $tpl_name;
    $theme_path = $config['site_path'] . '/themes/' . $config['theme'] . '/templates/' . $tpl_name;

    $is_mobile = (int)$_COOKIE['is_mobile'];
    if ($config['first_in'] == 1 && !$_COOKIE['is_mobile']) {
        $is_mobile = 1;
    }

    if ($is_mobile && file_exists($m_path)) {
        $path = $m_path;
    } elseif ($config['theme'] && file_exists($theme_path)) {
        $path = $theme_path;
        $new_design_on = 1;
    }

    $smarty->display($path);
}

// выбор изображения с учетом мобильности и разрешения экрана
function image_mobile($img_name, $only_path = false)
{
    global $config;
    $w = (int)$_COOKIE["screen_width"];
    $h = (int)$_COOKIE["screen_height"];
    $is_mobile = $config['is_mobile'];

    if ($w < 640) {
        $w = 320;
    } // приведение по ширине
    elseif ($w < 1024) {
        $w = 640;
    } else {
        $w = 1024;
    }

    if ($h < 480) {
        $h = 240;
    } // приведение по высоте
    elseif ($h < 768) {
        $h = 480;
    } else {
        $h = 768;
    }

    $path = '/images/mobile/' . $w . '_' . $h . '/' . $img_name;
    if (!file_exists($config['site_path'] . $path)) { // вариант по размеру не существует, используем общий вариант
        $path = '/images/mobile/common/' . $img_name;
        if (!file_exists($config['site_path'] . $path)) { // общий вариант не существует используем (desktop) вариант
            $path = '/images/' . $img_name;
        }
    }

    if ($only_path) {
        return $config['site_root'] . $path;
    } else {
        return '<img src="' . $config['site_root'] . $path . '" border="0">';
    }
}

// смена режима мобильный <-> компьютер
function set_mobile($is_mobile)
{
    global $config;

    if (!$_SESSION['mob_color_schemes']) {
        $my_res = sql_select_field(SCHEMES_TABLE, "color1, color3", "active = 1");
        $schemes = sql_fetch_assoc($my_res);
        $_SESSION['mob_color_schemes'] = $schemes;
    }

    if ($is_mobile) {
        setcookie('is_mobile', 1);
        $config['is_mobile'] = 1;
    } else {
        setcookie('is_mobile', 0);
        $config['is_mobile'] = 0;
    }
}

/**
 * @param int $cat_id идентификатор категории
 * @param int $table_id идентификатор таблицы
 * @param array $some_params дополнительные параметры
 *                 ['form_type']   тип формы (print,send,sms)
 *
 * @return array                возврат массива с данными навигации
 *                 ['cats']         категории
 *                 ['tables']       таблицы
 *                 ['reports']      представления
 *                 ['calendars']    календари
 *                 ['now_param']    название параметра таблицы, в котором мы находимся
 *                 ['count_params'] количество параметров
 *                 ['param_array']  массив с данными параметров таблицы,
 *                                  ключ - название параметра
 *                                  сортировка параметров по порядку определения
 *                    ['table_query'] - название таблицы с параметром
 *                    ['file_name']   - название файла с параметром
 *                    ['add_params']  - дополнительные параметры для ссылки на настройки параметров
 *                    ['add_query']   - дополнительные параметры для запроса таблицу параметра
 *                    ['count']       - количество настроенных позиций по параметру
 */
function get_navigation_data($cat_id = 0, $table_id = 0, $some_params = array())
{

    global $lang, $user, $config, $all_modules;

    if ($user['group_id'] != 1) {
        $sa_arr = array();
        if (is_array($user['sub_admin_rights']['tables'])) {
            foreach ($user['sub_admin_rights']['tables'] as $k => $v) {
                if ($v > 0) {
                    $q = sql_select(TABLES_TABLE, "id=", $k);
                    $d = sql_fetch_assoc($q);
                    if ($d) $sa_arr[] = $d['cat_id'];
                }
            }
        }
        if (is_array($user['sub_admin_rights']['reports'])) {
            foreach ($user['sub_admin_rights']['reports'] as $k => $v) {
                if ($v > 0) {
                    $q = sql_select(REPORTS_TABLE, "id=", $k);
                    $d = sql_fetch_assoc($q);
                    if ($d) $sa_arr[] = $d['cat_id'];
                }
            }
        }
        if (is_array($user['sub_admin_rights']['calendars'])) {
            foreach ($user['sub_admin_rights']['calendars'] as $k => $v) {
                if ($v > 0) {
                    $q = sql_select(CALENDARS_TABLE, "id=", $k);
                    $d = sql_fetch_assoc($q);
                    if ($d) $sa_arr[] = $d['cat_id'];
                }
            }
        }
        if (is_array($user['sub_admin_rights']['cats'])) {
            foreach ($user['sub_admin_rights']['cats'] as $k => $v) {
                if ($v > 0) {
                    if ($d) $sa_arr[] = $k;
                }
            }
        }
        $sa_cats = implode(", ", $sa_arr);
        if ($sa_cats) {
            $sa_cats = "AND id in (" . $sa_cats . ")";
        } else {
            $sa_cats = "AND id=0";
        }

        $sa_arr = array();
        if (is_array($user['sub_admin_rights']['tables'])) {
            foreach ($user['sub_admin_rights']['tables'] as $k => $v) {
                if ($v > 0) {
                    $sa_arr[] = $k;
                }
            }
        }
        $sa_tables = implode(", ", $sa_arr);
        if ($sa_tables) {
            $sa_tables = "AND id in (" . $sa_tables . ")";
        } else {
            $sa_tables = "AND id=0";
        }

        $sa_arr = array();
        if (is_array($user['sub_admin_rights']['reports'])) {
            foreach ($user['sub_admin_rights']['reports'] as $k => $v) {
                if ($v > 0) {
                    $sa_arr[] = $k;
                }
            }
        }
        $sa_reports = implode(", ", $sa_arr);
        if ($sa_reports) {
            $sa_reports = "AND id in (" . $sa_reports . ")";
        } else {
            $sa_reports = "AND id=0";
        }

        $sa_arr = array();
        if (is_array($user['sub_admin_rights']['calendars'])) {
            foreach ($user['sub_admin_rights']['calendars'] as $k => $v) {
                if ($v > 0) {
                    $sa_arr[] = $k;
                }
            }
        }
        $sa_calendars = implode(", ", $sa_arr);
        if ($sa_calendars) {
            $sa_calendars = "AND id in (" . $sa_calendars . ")";
        } else {
            $sa_calendars = "AND id=0";
        }
    }

    $data_final = array();

    $cat = (int)$cat_id;
    $table = (int)$table_id;

    // ---------- получаем категории
    $data_final['cats'] = array();
    $tables_not = '';
    $cats_not = '';
    $addon = sql_select(ADDONS_TABLE, 'inactive=1');
    while ($addons = sql_fetch_assoc($addon)) {
        $addon_part = sql_select(ADDON_PARTS_TABLE, 'part_type="table" and addon_id=', $addons['id']);
        while ($addons_part = sql_fetch_assoc($addon_part)) {
            $tables_not .= ' and id!=' . $addons_part['part_id'] . ' ';
            $cats = sql_select_array(TABLES_TABLE, 'id=', $addons_part['part_id']);
            if ($cats['cat_id']) {
                $cats_not .= ' and id!=' . $cats['cat_id'] . ' ';
            }
        }
    }
    $result = sql_select_field(CATS_TABLE, "id,name", "1=1 " . $cats_not . " " . $sa_cats . " ORDER BY name");
    while ($row = sql_fetch_assoc($result)) {
        $data_final['cats'][$row['id']] = form_display($row['name']);
    }

    // ---------- получаем таблицы и представления

    // если установлена категория, фильтруем таблицы по категории
    if ($cat) {
        $cat_condition = " AND cat_id=" . $cat;
    }

    // таблицы
    $data_final['tables'] = array();
    $result = sql_select_field(TABLES_TABLE, "id,name_table",
        "1=1  " . $tables_not . " " . $cat_condition . " " . $sa_tables . " ORDER BY " . ($cat ? "table_num" : "name_table"));
    while ($row = sql_fetch_assoc($result)) {
        $data_final['tables'][$row['id']] = form_display($row['name_table']);
    }

    // представления
    $data_final['reports'] = array();
    $result = sql_select_field(REPORTS_TABLE, "id,name",
        "1=1" . $cat_condition . " " . $sa_reports . " ORDER BY " . ($cat ? "num" : "name"));
    while ($row = sql_fetch_assoc($result)) {
        $data_final['reports'][$row['id']] = $row['name'];
    }

    // календари
    $data_final['calendars'] = array();
    $result = sql_select_field(CALENDARS_TABLE, "id,name",
        "1=1" . $cat_condition . " " . $sa_calendars . " ORDER BY " . ($cat ? "num" : "name"));
    while ($row = sql_fetch_assoc($result)) {
        $data_final['calendars'][$row['id']] = $row['name'];
    }

    // расширения
    $extensions = (\CB\Core::get(\CB\Core::EXTENSION_MANAGER))->getExtensionList();

    // получаем настройки выбранной таблицы
    if ($table > 0) {
        $data_final['params'] = array();

        // определяем массив настроек и их таблицы
        $configs_array = array(
            $lang['Common'] => array('file_name' => 'edit_table.php')
        );
        $configs_array[$lang['fields']] = array(
            'table_query' => FIELDS_TABLE,
            'file_name' => 'edit_field.php',
            'type' => 'field'
        );
        $configs_array[$lang['formatting']] = array(
            'table_query' => FORMAT_TABLE,
            'file_name' => 'edit_format.php',
            'add_query' => ' AND arc = 0',
            'type' => 'format'
        );
        $configs_array[$lang['filters']] = array(
            'table_query' => FILTERS_TABLE,
            'file_name' => 'edit_filter.php',
            'add_query' => ' AND arc = 0',
            'type' => 'filter'
        );
        $configs_array[$lang['adds_buttons']] = array(
            'table_query' => BUTTONS_TABLE,
            'file_name' => 'edit_button.php',
            'add_query' => ' AND arc = 0',
            'type' => 'button'
        );
        $configs_array[$lang['computation']] = array(
            'table_query' => CALC_TABLE,
            'file_name' => 'edit_calc.php',
            'type' => 'calc'
        );
        $configs_array[$lang['questionaries']] = array(
            'table_query' => QST_TABLE,
            'file_name' => 'edit_questionare.php',
            'add_query' => ' AND arc = 0',
            'type' => 'questionare'
        );
        if ($user['group_id'] == 1 || $user['sub_admin_rights']['addit_sync']) {
            $configs_array[$lang['Sync']] = array(
                'file_name' => 'edit_sync.php'
            );
        }
        $configs_array[$lang['print_templates']] = array(
            'table_query' => FORMS_TABLE,
            'file_name' => 'forms.php',
            'add_params' => "&mode=print&admin",
            'add_query' => " AND arc = 0 AND dest_form='print'",
            'type' => 'form'
        );
        $configs_array[$lang['Mail_templates']] = array(
            'table_query' => FORMS_TABLE,
            'file_name' => 'forms.php',
            'add_params' => "&mode=send&admin",
            'add_query' => " AND arc = 0 AND dest_form='send'",
            'type' => 'form'
        );
        if ($all_modules['sms']['status'] == 0) {
            $configs_array[$lang['SMS_templates']] = array(
                'table_query' => FORMS_TABLE,
                'file_name' => 'forms.php',
                'add_params' => "&mode=sms&admin",
                'add_query' => " AND arc = 0 AND dest_form='sms'",
                'type' => 'form'
            );
        }
        if ($extensions['cbext.clientbase.wazzup']['is_active']) {
            $configs_array[$lang['Msg_templates']] = array(
                'table_query' => FORMS_TABLE,
                'file_name' => 'forms.php',
                'add_params' => "&mode=msg&admin",
                'add_query' => " AND arc = 0 AND dest_form='msg'",
                'type' => 'form'
            );
        }
        $configs_array[$lang['notifiers']] = array(
            'table_query' => TIPS_TABLE,
            'file_name' => 'edit_tip.php',
            'add_query' => ' AND arc = 0',
            'type' => 'tip'
        );
        $configs_array[$lang['subtables']] = array(
            'table_query' => SUBTABLES_TABLE,
            'file_name' => 'edit_subtable.php',
            'add_query' => ' AND arc = 0',
            'type' => 'subtable'
        );
        $configs_array[$lang['addittables']] = array(
            'table_query' => ADDIT_TABLES_TABLE,
            'file_name' => 'edit_addittable.php',
            'add_query' => ' AND arc = 0',
            'type' => 'addittable'
        );
        $configs_array[$lang['informers']] = array(
            'table_query' => INFORMERS_TABLE,
            'file_name' => 'edit_status.php',
            'add_query' => ' AND arc = 0',
            'type' => 'informer'
        );

        $data_final['param_array'] = $configs_array;

        // проходим по всем настройкам
        foreach ($configs_array as $param_name => $param_table) {
            if ($param_table['file_name'] == 'edit_calc.php' && $param_name == $lang['computation']) {
                $my_count = 0;
                $result = sql_select(CALC_TABLE, 'table_id=', $table_id, ' AND arc = 0 ORDER BY id');
                while ($calc = sql_fetch_array($result)) { //Выбираем все вычисления данной таблицы
                    $calc_id = $calc['id'];
                    $result2 = sql_select(CALC_COND_TABLE, "calc_id='", $calc_id, "'");
                    $dont_show = 0;
                    while ($cond = sql_fetch_array($result2)) { // Выбираем все условия данной таблицы
                        $calc['conditions'] = $cond;
                        // Не выводим вычисления в кнопках
                        if (($cond['type'] != EVT_LINE_SAVE) && ($cond['type'] != EVT_FIELD_SHOW) && ($cond['type'] != EVT_FIELD_CHANGE) &&
                            ($cond['type'] != EVT_TABLE_IMPORT) && ($cond['type'] != EVT_LINE_DROP) && ($cond['type'] != EVT_LINE_RESTORE) &&
                            ($cond['type'] != EVT_SCHEDULE)
                        ) {
                            $dont_show = 1;
                            break;
                        }

                    }
                    if ($dont_show) {
                        continue;
                    } // Не выводим вычисление
                    $my_count++;
                }
                $row['cnt'] = $my_count;
            } elseif ($param_table['file_name'] == 'edit_sync.php') {
                $table_sync = [];
                $table_fields = get_table_fields(get_table($table_id));
                $result = sql_select_field(SYNC_FIELDS_TABLE, 'field_id, sync_id');
                while ($row = sql_fetch_array($result)) {
                    if ($table_fields[$row['field_id']]) {
                        $table_sync[$row['sync_id']] = 1;
                    }
                }
                $row['cnt'] = count($table_sync);
            } elseif ($param_table['table_query']) {
                $result = sql_select_field($param_table['table_query'], "COUNT(*) as cnt", "table_id=", $table, $param_table['add_query']);
                $row = sql_fetch_assoc($result);
            } else {
                $row['cnt'] = 0;
            }
            // вычитаем защищенные, если включено скрытие защищенных
            if ($config['protect_of_elements'] && !$config['display_protected_elements']) {
                $group_cond = $user['id'] == 1 ? 'group_id = 0' : '(group_id = ' . $user['group_id'] . ' OR group_id = 0)';
                if ($param_table['type'] == 'form') {
                    $result2 = sql_select_field($param_table['table_query'], 'id', "table_id=", $table, $param_table['add_query']);
                    while ($row2 = sql_fetch_assoc($result2)) {
                        $result3 = sql_select_field(PROTECT_TABLE, 'view', "elem_type = 'form' AND elem_id = '", $row2['id'], "' AND $group_cond");
                        $row3 = sql_fetch_assoc($result3);
                        if ($row3 && $row3['view'] == 0) {
                            $row['cnt']--;
                        }
                    }
                } else {
                    $result = sql_select_field(PROTECT_TABLE, "COUNT(*) as cnt", "table_id = $table AND elem_type = '", $param_table['type'], "' AND elem_id > 0 AND view = 0 AND $group_cond");
                    $row2 = sql_fetch_assoc($result);
                    $row['cnt'] -= $row2['cnt'];
                }
            }
            $data_final['param_array'][$param_name]['count'] = $row['cnt'];
        }

        $data_final['count_params'] = count($configs_array);

        // определяем в какой настройке мы находимся
        $file_properties = pathinfo($_SERVER['SCRIPT_NAME']);
        foreach ($configs_array as $name => $data) {
            if ($data['file_name'] == $file_properties['basename']
                && $some_params['form_type']
                && $data['add_params']
                && stripos($data['add_params'], trim($some_params['form_type'])) !== false
            ) {
                $data_final['now_param'] = $name;
                break;
            } elseif (!$some_params['form_type'] && $data['file_name'] == $file_properties['basename']) {
                $data_final['now_param'] = $name;
                break;
            }
        }
    }

    $data_final['param_array'] = $data_final['param_array'] ?? [];
    $row = sql_select_array(ACC_TABLES_TABLE, "group_id=", $user['group_id'], " AND table_id=", $table_id);
    if ($row["free_mail_template"] == '1') {
        $data_final['param_array'][$lang['Mail_templates']]['count'] = (int)$data_final['param_array'][$lang['Mail_templates']]['count'] + 1;
    }
    if ($row["free_sms_template"] == '1') {
        $data_final['param_array'][$lang['SMS_templates']]['count'] = (int)$data_final['param_array'][$lang['SMS_templates']]['count'] + 1;
    }
    // вот и все
    return $data_final;
}

/**
 * Генерация случайного значения
 *
 * @param int $field_id идентификатор поля
 * @param bool $mult мульти выбор
 *
 * @return mixed возвращает случайное значение поля
 */
function field_random($field_id, $mult = false)
{
    global $user;
    $row = get_field($field_id);
    $type_field = $row['type_field'];
    $values = array();
    $value = '';

    if ($type_field == 4) // список
    {
        $type_value = explode("\r\n", $row['type_value']);
        $values = $type_value;
        $count_values = count($values);
        if ($mult) {
            $value = array();
            $n = rand(0, $count_values - 1);
            for ($i = 0; $i <= $n; $i++) {
                $value_for_checked = $values[rand(0, $count_values - 1)];
                if (in_array($value_for_checked, $value)) {
                    $i--;
                } else {
                    $value[] = $value_for_checked;
                }
            }
            $value = implode("\r\n", $value);
        } else {
            $value = $values[rand(0, $count_values - 1)];
        }
    } elseif ($type_field == 7) // пользователь
    {
        if ($row['type_value']) {
            $type_value = str_replace('{current}', $user['group_id'] ?: 0, $row['type_value']);
            $type_value = array_unique(explode("|", $type_value));
            $type_value = array_diff($type_value, array(''));
            $cond_users = 'arc=0 AND group_id IN (' . implode(',', $type_value) . ") ";
        } else {
            $cond_users = 'arc=0 ';
        }
        if ($mult) {
            $result = sql_select_field(USERS_TABLE, "id", $cond_users);
            while ($row = sql_fetch_assoc($result)) {
                $values[] = $row['id'];
            }
            $count_values = count($values);
            $value = array();
            $n = rand(0, $count_values - 1);
            for ($i = 0; $i <= $n; $i++) {
                $value_for_checked = $values[rand(0, $count_values - 1)];
                if (in_array($value_for_checked, $value)) {
                    $i--;
                } else {
                    $value[] = $value_for_checked;
                }
            }
            $value = "-" . implode("-", $value) . "-";
        } else {
            $result = sql_select_field(USERS_TABLE, "id", $cond_users . " ORDER BY RAND() LIMIT 1");
            $row = sql_fetch_assoc($result);
            $value = $row['id'];
        }
    } elseif ($type_field == 14) // группа
    {
        $type_value = explode("|", $row['type_value']);
        if ($type_value[2]) {
            $type_value = array_unique(explode(",", $type_value[2]));
            $type_value = array_diff($type_value, array(''));
            $cond_groups = 'hidden=0 AND id IN (' . implode(',', $type_value) . ") ";
        } else {
            $cond_groups = 'hidden=0 ';
        }
        if ($mult) {
            $result = sql_select_field(GROUPS_TABLE, "id", $cond_groups);
            while ($row = sql_fetch_assoc($result)) {
                $values[] = $row['id'];
            }
            $count_values = count($values);
            $value = array();
            $n = rand(0, $count_values - 1);
            for ($i = 0; $i <= $n; $i++) {
                $value_for_checked = $values[rand(0, $count_values - 1)];
                if (in_array($value_for_checked, $value)) {
                    $i--;
                } else {
                    $value[] = $value_for_checked;
                }
            }
            $value = "-" . implode("-", $value) . "-";
        } else {
            $result = sql_select_field(GROUPS_TABLE, "id", $cond_groups . " ORDER BY RAND() LIMIT 1");
            $row = sql_fetch_assoc($result);
            $value = $row['id'];
        }
    } elseif ($type_field == 5) // связь
    {
        $type_value = explode("|", $row['type_value']);
        $link_table_id = (int)$type_value[0];
        $link_field_id = (int)$type_value[1];
        $link_filter_id = (int)$type_value[2];
        $link_target_field = form_int_name($link_field_id);

        if (!empty($link_table_id) && !empty($link_target_field)) {
            $filter = '';
            if ($link_filter_id) {
                $filter_value = sql_fetch_row(sql_select_field(FILTERS_TABLE, 'value', 'id = ', $link_filter_id))[0];
                if ($filter_value) {
                    if (strpos($filter_value, "{{{{GROUPBY ") !== false) { // вырезаем {GROUPBY}
                        $filter_value = substr($filter_value, 0, strpos($filter_value, "{{{{GROUPBY ")) . substr($filter_value, (strpos($filter_value, "}}}}") + 4));
                    }
                    $filter = '';
                    if (!empty($filter_value)) {
                        $filter = ' AND (' . $filter_value . ')';
                    }
                }
            }
            $table = DATA_TABLE . $link_table_id;
            /// Выбираем случайное значение оптимальным способом для больших таблиц, без ORDER BY RAND()
            $sql = <<<SQL
            SELECT f.id FROM $table f
            JOIN (SELECT RAND() * (SELECT MAX(id) FROM $table) AS max_id) AS m
            WHERE f.id >= m.max_id
            AND status = 0 AND $link_target_field<>'' $filter
            ORDER BY f.id ASC
            LIMIT 1;
SQL;
            $result = sql_query($sql);
            $row = sql_fetch_assoc($result);
            $value = $row['id'];
        } else {
            $value = 0;
        }
    } elseif ($type_field == 1) // число
    {
        $type_value = explode("|", $row['type_value']);
        $type_number = explode("/", $type_value[0]);
        $min = $type_value[1];
        $max = $type_number[0];
        $value = '';
        $n = rand($min, $max);
        for ($i = 0; $i < $n; $i++) {
            $value .= rand(0, 9);
        }
        if ($type_number[1] != 0) {
            $value = (int)$value / (pow(10, $type_number[1]));
        }
    }
    return $value;
}

/**
 * Генерация значения по порядку
 *
 * @param int $field_id идентификатор поля
 * @param bool $mult мульти выбор
 *
 * @return mixed возвращает следующее значение поля
 */
function field_inorder($field_id, $mult = false)
{
    if ($mult) return '';

    global $user, $config;
    $row = get_field($field_id);
    $type_field = $row['type_field'];
    $last_value = $row['last_value'];
    if($last_value == null){
        $res = sql_query("SELECT * FROM " . $config['table_prefix'] . "fields WHERE id='" . $field_id . "'");
        if($row = sql_fetch_assoc($res)){
            $last_value = $row['last_value'];
        }
    }
    if ($last_value[0] == '-') {
        $last_value = explode('-', trim($last_value, '-'))[0];
    }
    $value = '';

    if ($type_field == 4) // список
    {
        $type_value = explode("\r\n", $row['type_value']);
        $values = $type_value;
        $count_values = count($values);
        if (($count_values - 1) < (array_search($last_value, $values) + 1)) {
            $value = $values[0];
        } else {
            $value = $values[array_search($last_value, $values) + 1];
        }
    } elseif ($type_field == 7) // пользователь
    {
        if ($row['type_value']) {
            $type_value = str_replace('{current}', $user['group_id'] ?: 0, $row['type_value']);
            $type_value = array_unique(explode("|", $type_value));
            $type_value = array_diff($type_value, array(''));
            $cond_users = 'arc=0 AND group_id IN (' . implode(',', $type_value) . ") ";
        } else {
            $cond_users = 'arc=0 ';
        }
        if (is_numeric($last_value)) {
            $result = sql_select_field(USERS_TABLE, "id", $cond_users . " AND id > $last_value ORDER BY id LIMIT 1");
            $row = sql_fetch_assoc($result);
            if (isset($row['id'])) {
                $value = $row['id'];
            }
        }
        if (empty($value)) {
            $result = sql_select_field(USERS_TABLE, "id", $cond_users . " ORDER BY id LIMIT 1");
            $row = sql_fetch_assoc($result);
            if (isset($row['id'])) {
                $value = $row['id'];
            }
        }
    } elseif ($type_field == 14) // группа
    {
        $type_value = explode("|", $row['type_value']);
        if ($type_value[2]) {
            $type_value = array_unique(explode(",", $type_value[2]));
            $type_value = array_diff($type_value, array(''));
            $cond_groups = 'hidden=0 AND id IN (' . implode(',', $type_value) . ") ";
        } else {
            $cond_groups = 'hidden=0 ';
        }
        if ($last_value && $last_value != '{inorder}') {
            $result = sql_select_field(GROUPS_TABLE, "id", $cond_groups . " AND id > $last_value ORDER BY id LIMIT 1");
            $row = sql_fetch_assoc($result);
            if (isset($row['id'])) {
                $value = $row['id'];
            }
        }
        if (empty($value)) {
            $result = sql_select_field(GROUPS_TABLE, "id", $cond_groups . " ORDER BY id LIMIT 1");
            $row = sql_fetch_assoc($result);
            if (isset($row['id'])) {
                $value = $row['id'];
            }
        }
    } elseif ($type_field == 5) // связь
    {
        $last_value = (int)$last_value;
        $type_value = explode("|", $row['type_value']);
        $link_table_id = (int)$type_value[0];
        $link_field_id = (int)$type_value[1];
        $link_filter_id = (int)$type_value[2];
        $link_target_field = form_int_name($link_field_id);

        if (!empty($link_table_id) && !empty($link_target_field)) {
            $filter = '';
            if ($link_filter_id) {
                $filter_value = sql_fetch_row(sql_select_field(FILTERS_TABLE, 'value', 'id = ', $link_filter_id))[0];
                if ($filter_value) {
                    $filter = ' AND (' . $filter_value . ')';
                }
            }
            if ($last_value) {
                $result = data_select_field($link_table_id, "id", $link_target_field . "<>'' AND status = 0 AND id > $last_value $filter ORDER BY id LIMIT 1");
                $row = sql_fetch_assoc($result);
                if (isset($row['id'])) {
                    $value = $row['id'];
                }
            }
            if (empty($value)) {
                $result = data_select_field($link_table_id, "id", $link_target_field . "<>'' AND status = 0 $filter ORDER BY id LIMIT 1");
                $row = sql_fetch_assoc($result);
                if (isset($row['id'])) {
                    $value = $row['id'];
                }
            }
        } else {
            $value = 0;
        }
    }
    return $value;
}

/**
 * Возвращает конфигурацию поля таблицы
 * Использует глобальный кеш таблиц, и локальный статический кеш полей
 *
 * @param int|string $fieldId Идентификатор поля таблицы. Возможно значение с префиксом f
 * @return array|null
 */
function get_field($fieldId)
{
    global $tables_cache;
    static $fields = [];

    $fieldId = ltrim($fieldId, 'f');

    foreach ($tables_cache as $table) {
        if (isset($table['table_fields'][$fieldId])) {
            return $table['table_fields'][$fieldId];
        }
    }

    if (isset($fields[$fieldId])) {
        return $fields[$fieldId];
    }

    $q = sql_select(FIELDS_TABLE, 'id=', $fieldId);
    $row = sql_fetch_array($q);
    sql_free_result($q);
    if (is_array($row) && isset($row['id'])) {
        $fields[$row['id']] = $row;
    }

    return $row;
}

function cb_alert()
{
    global $config, $user;

    $config['cb_alert'] = sql_select_array(CONFIG_TABLE, "name='cb_alert'")['value'];
    // Получаем из оповешение базы или создаем новое
    if ($config['cb_alert']) {
        $array_cb_alert = json_decode($config['cb_alert'], true);
    } else {
        $array_cb_alert = array();
        $array_cb_alert['date_last_update'] = '0000-00-00 00:00:00';
        $array_cb_alert['period_update'] = "+ 1 day";
    }

    // Получаем оповещение с менеджа если время подходит по периоду обновления
    if (strtotime($array_cb_alert['date_last_update'] . " " . $array_cb_alert['period_update']) < strtotime(date("Y-m-d H:i:s"))) {
        // Параметры запроса
        $array_get_cb_alert = array(
            'type' => $config['type'],
            'url' => $config['site_url'],
            'version' => $config['version'],
        );

        if ($config['type'] != 'SAAS') {
            $array_get_cb_alert['login'] = substr(key_decrypt(file_get_contents($config['site_path'] . '/runtime/key.php')), 16, 14);
        }

        $data_string = json_encode($array_get_cb_alert);

        // Отправка запроса
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, 'https://manage.' . CLIENTBASE_DOMAIN . '/client_alert.php');
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
        curl_setopt($ch, CURLOPT_TIMEOUT, 15);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($data_string)
            )
        );
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_POST, 1);
        if ($config["proxy_host"]) {
            curl_setopt($ch, CURLOPT_PROXY,
                $config["proxy_host"] . ($config["proxy_port"] ? ':' . $config["proxy_port"] : ''));
        }
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        $result = curl_exec($ch);

        // Получение и обработка данных
        if ($result_json = json_decode($result, true)) {
            if ($result_json['code'] == 0) {
                if ($result_json['data']['id'] != $array_cb_alert['id'] && $result_json['data']['text']) {
                    // id, загаловок, текст
                    $array_cb_alert['id'] = $result_json['data']['id'];
                    $array_cb_alert['title'] = $result_json['data']['title'];
                    $array_cb_alert['text'] = $result_json['data']['text'];
                    $array_cb_alert['button_name'] = $result_json['data']['button_name'];
                    $array_cb_alert['button_url'] = $result_json['data']['button_url'];
                    $array_cb_alert['url_img'] = $result_json['data']['url_img'];
                    $array_cb_alert['type_pattern'] = $result_json['data']['type_pattern'];

                    // Права доступа, т.е. кому показывать, а кому нет.
                    $array_cb_alert['show'] = array();
                    if ($result_json['data']['show']) {
                        if ($config['cb_alert_show'][1] == true && $result_json['data']['show'][1] == true) {
                            $array_cb_alert['show'][1] = true;
                        } else {
                            $array_cb_alert['show'][1] = false;
                        }
                        if ($config['cb_alert_show'][777] == true && $result_json['data']['show'][777] == true) {
                            $array_cb_alert['show'][777] = true;
                        } else {
                            $array_cb_alert['show'][777] = false;
                        }
                        if ($config['cb_alert_show'][999] == true && $result_json['data']['show'][999] == true) {
                            $array_cb_alert['show'][999] = true;
                        } else {
                            $array_cb_alert['show'][999] = false;
                        }
                    }
                    $array_cb_alert['user_show'] = array();
                    $result = sql_select_field(USERS_TABLE, "id, group_id", "arc=0 ORDER BY group_id ASC");
                    while ($row = sql_fetch_assoc($result)) {
                        if ($row['group_id'] == 1) {
                            if ($array_cb_alert['show'][1] == true) {
                                $array_cb_alert['user_show'][$row['id']] = true;
                            } else {
                                $array_cb_alert['user_show'][$row['id']] = false;
                            }
                        } else if ($row['group_id'] == 777) {
                            if ($array_cb_alert['show'][777] == true) {
                                $array_cb_alert['user_show'][$row['id']] = true;
                            } else {
                                $array_cb_alert['user_show'][$row['id']] = false;
                            }
                        } else {
                            if ($array_cb_alert['show'][999] == true) {
                                $array_cb_alert['user_show'][$row['id']] = true;
                            } else {
                                $array_cb_alert['user_show'][$row['id']] = false;
                            }
                        }
                    }
                }
                // Ссылка на картинку через которую считаем просмотры
                if ($result_json['data']['show_url_img']) {
                    $array_cb_alert['show_url_img'] = $result_json['data']['show_url_img'];
                }
                // Преиод обновления
                if ($result_json['data']['period_update']) {
                    $array_cb_alert['period_update'] = $result_json['data']['period_update'];
                }
            }
        }

        $array_cb_alert['date_last_update'] = date("Y-m-d H:i:s");

        updateCbAlertData($array_cb_alert);
        $config['cb_alert'] = $array_cb_alert;
    }

    // Отмечаем у себя в базе что данный пользователь уже просмотрел данное оповешение и не выводим его ему больше.
    if ($_GET['cb_alert_hash']) {
        $image = imagecreatetruecolor(1, 1); // создаем холст 1 на 1 пиксель
        imagefill($image, 0, 0, 0xFFFFFF); // делаем его белым
        header('Content-type: image/png'); // задаем заголовок
        imagepng($image); // выводим картинку
        imagedestroy($image); // очищаем память от картинки

        // ведем статистику
        $array_cb_alert['user_show'][$user['id']] = false;

        updateCbAlertData($array_cb_alert);
        $config['cb_alert'] = $array_cb_alert;
    }


    // Параметры для учета просмотров.
    if ($array_cb_alert['user_show'][$user['id']]) {
        $array_cb_alert['hash'] = array(
            'id' => $array_cb_alert['id'],
            'user_id' => $user['id'],
            'group_id' => $user['group_id'],
            'type' => $config['type'],
            'url' => $config['site_url']
        );
        $array_cb_alert['base64_hash'] = base64_url_encode(json_encode($array_cb_alert['hash']));
        $array_cb_alert['show_url_img'] = $array_cb_alert['show_url_img'] . "?hash=" . $array_cb_alert['base64_hash'];
        $array_cb_alert['text'] = $array_cb_alert['text'];
        $array_cb_alert['check_show'] = "<img src='" . $array_cb_alert['show_url_img'] . "'><img src='?cb_alert_hash=" . $array_cb_alert['base64_hash'] . "'>";
        unset($array_cb_alert['show_url_img']);
        unset($array_cb_alert['hash']);

        return json_encode($array_cb_alert);
    } else {
        return "";
    }
}

function updateCbAlertData($array_cb_alert)
{
    $result = sql_select_field(CONFIG_TABLE, "name", "name='cb_alert'");
    if (!sql_num_rows($result)) {
        sql_insert(CONFIG_TABLE, array('name' => 'cb_alert', 'value' => json_encode($array_cb_alert)));
    } else {
        sql_update(CONFIG_TABLE, array('value' => json_encode($array_cb_alert)), "name='cb_alert'");
    }
}

// Заменяем новые переменные полей и подтаблиц на внешние имена
function replace_variables_to_external($template, $table_fields, $table_id)
{
    global $lang;

    $template = str_replace("{\$_NumberOfRecord}", "{\$* " . $lang["Line_number"] . " *}", $template);
    $qst_forms = sql_select_array(QST_TABLE, 'ALL_ROWS', 'table_id=', $table_id);
    foreach ($qst_forms as $qst) {
        $template = str_replace('{$_QstEditLink[' . $qst['id'] . ']}',
            '{$' . $lang['Questionaries'] . '.' . htmlspecialchars($qst['name']) . '?hash}', $template);
        $template = str_replace('{$_QstLink[' . $qst['id'] . ']}',
            '{$' . $lang['Questionaries'] . '.' . htmlspecialchars($qst['name']) . '}', $template);
    }
    $re = '/\{\$_QstE?d?i?t?Link\[\d+\]\}/';
    preg_replace($re, '', $template);

    foreach ($table_fields as $one_field) {
        $one_field['name_field'] = form_display($one_field['name_field']);
        if ($one_field['type_field'] == 5) {
            $table_id2 = $one_field['s_table_id'];
            $table2 = get_table($table_id2);
            $table_fields2 = get_table_fields($table2);
            foreach ($table_fields2 as $one_field2) {
                $one_field2['name_field'] = form_display($one_field2['name_field']);
                $template = str_replace("{\$field" . $one_field['id'] . ".field" . $one_field2['id'] . "}",
                    "{\$" . $one_field['name_field'] . "." . $one_field2['name_field'] . "}", $template);
                if ($one_field2['summa']) {
                    $template = str_replace("{\$field" . $one_field['id'] . ".sum_field" . $one_field2['id'] . "}",
                        "{\$" . $one_field['name_field'] . "." . $lang["Sum"] . " (" . $one_field2['name_field'] . ")}",
                        $template);
                }
            }
        } else {
            $template = str_replace("{\$field" . $one_field['id'] . "}", "{\$" . $one_field['name_field'] . "}",
                $template);
            if ($one_field['summa']) {
                $template = str_replace("{\$sum_field" . $one_field['id'] . "}",
                    "{\$" . $lang["Sum"] . " (" . $one_field['name_field'] . ")}", $template);
            }
        }
        if ($one_field['type_field'] == 7) {
            $template = str_replace("{\$ufield" . $one_field['id'] . ".e_mail}", "{\$" . $one_field['name_field'] . ".E-mail}", $template);
            $template = str_replace("{\$ufield" . $one_field['id'] . ".phone}", "{\$" . $one_field['name_field'] . ".Телефон}", $template);
        }
    }

    $result = sql_select(SUBTABLES_TABLE, "table_id=", $table_id);
    while ($subtable = sql_fetch_assoc($result)) {
        $template = str_replace('{foreach from="{$subtable' . $subtable['id'] . '}" name="subtable' . $subtable['id'] . '" item=subtable}',
            '{foreach from="{$' . $subtable['name'] . '}" name="' . $subtable['name'] . '" item=subtable}', $template);
        $template = str_replace('$smarty.foreach.subtable' . $subtable['id'] . '.',
            '$smarty.foreach.' . $subtable['name'] . '.', $template);
        $sub_fields = get_table_fields(get_table($subtable['link_table_id']));
        foreach ($sub_fields as $k => $one_field) {
            $one_field['name_field'] = form_display($one_field['name_field']);
            $template = str_replace("{\$subtable.field" . $one_field['id'] . "}",
                "{\$" . $subtable['name'] . "." . $one_field['name_field'] . "}", $template);
            if ($one_field['summa']) {
                $template = str_replace("{\$subtable.sum_field" . $one_field['id'] . "}",
                    "{\$" . $subtable['name'] . "." . $lang["Sum"] . " (" . $one_field['name_field'] . ")}", $template);
            }
        }
    }

    return $template;
}

// Заменяем новые переменные полей и подтаблиц на внутренние имена
function replace_variables_to_internal($template, $table_fields, $table_id)
{
    global $lang;

    $template = str_replace("{\$* " . $lang["Line_number"] . " *}", "{\$_NumberOfRecord}", $template);
    $qst_forms = sql_select_array(QST_TABLE, 'ALL_ROWS', 'table_id=', $table_id);
    foreach ($qst_forms as $qst) {
        $template = str_replace("{\${$lang['Questionaries']}." . htmlspecialchars($qst['name']) . "+hash}",
            "{\$_QstEditLink[{$qst['id']}]}", $template);
        $template = str_replace("{\${$lang['Questionaries']}." . htmlspecialchars($qst['name']) . "}",
            "{\$_QstLink[{$qst['id']}]}", $template);
    }

    // вначале обрабатываем подтаблицы, во избежании коллизий с совпадающими именами полей
    $result = sql_select(SUBTABLES_TABLE, "table_id=", $table_id);
    while ($subtable = sql_fetch_assoc($result)) {
        $template = str_replace('{foreach from="{$' . $subtable['name'] . '}" name="' . $subtable['name'] . '" item=subtable}',
            '{foreach from="{$subtable' . $subtable['id'] . '}" name="subtable' . $subtable['id'] . '" item=subtable}',
            $template);
        $template = str_replace('$smarty.foreach.' . $subtable['name'] . '.',
            '$smarty.foreach.subtable' . $subtable['id'] . '.', $template);
        $sub_fields = get_table_fields(get_table($subtable['link_table_id']));
        foreach ($sub_fields as $k => $one_field) {
            $template = str_replace("{\$" . $subtable['name'] . "." . $one_field['name_field'] . "}",
                "{\$subtable.field" . $one_field['id'] . "}", $template);
            if ($one_field['summa']) {
                $template = str_replace("{\$" . $subtable['name'] . "." . $lang["Sum"] . " (" . $one_field['name_field'] . ")}",
                    "{\$subtable.sum_field" . $one_field['id'] . "}", $template);
            }
        }
    }

    foreach ($table_fields as $one_field) {
        if ($one_field['type_field'] == 5) {
            $table_id2 = $one_field['s_table_id'];
            $table2 = get_table($table_id2);
            $table_fields2 = get_table_fields($table2);
            foreach ($table_fields2 as $one_field2) {
                $template = str_replace("{\$" . $one_field['name_field'] . "." . $one_field2['name_field'] . "}",
                    "{\$field" . $one_field['id'] . ".field" . $one_field2['id'] . "}", $template);
                if ($one_field2['summa']) {
                    $template = str_replace("{\$" . $one_field['name_field'] . "." . $lang["Sum"] . " (" . $one_field2['name_field'] . ")}",
                        "{\$field" . $one_field['id'] . ".sum_field" . $one_field2['id'] . "}", $template);
                }
            }
        } else {
            $template = str_replace("{\$" . $one_field['name_field'] . "}", "{\$field" . $one_field['id'] . "}",
                $template);
            if ($one_field['summa']) {
                $template = str_replace("{\$" . $lang["Sum"] . " (" . $one_field['name_field'] . ")}",
                    "{\$sum_field" . $one_field['id'] . "}", $template);
            }
        }
        if ($one_field['type_field'] == 7) {
            $template = str_replace("{\$" . $one_field['name_field'] . ".E-mail}", "{\$ufield" . $one_field['id'] . ".e_mail}", $template);
            $template = str_replace("{\$" . $one_field['name_field'] . ".Телефон}", "{\$ufield" . $one_field['id'] . ".phone}", $template);
        }
    }

    return $template;
}

function normalize_path($path)
{
    $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
    $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
    $absolutes = array();
    foreach ($parts as $part) {
        if ('.' == $part) {
            continue;
        }
        if ('..' == $part) {
            array_pop($absolutes);
        } else {
            $absolutes[] = $part;
        }
    }
    return implode(DIRECTORY_SEPARATOR, $absolutes);
}

function check_ext_name($ext_name)
{
    global $config;

    if (stristr(normalize_path($config['site_path'] . DIRECTORY_SEPARATOR . $ext_name), normalize_path($config['site_path'])) === false) {
        return false;
    }
    if (check_filename($ext_name) === false) {
        return false;
    }
    return true;
}

function get_ext_name($ext_name)
{
    global $config;

    $ext_name = str_replace(normalize_path($config['site_path']) . DIRECTORY_SEPARATOR, "",
        normalize_path(normalize_path($config['site_path']) . DIRECTORY_SEPARATOR . $ext_name));
    if (strncmp(PHP_OS, 'WIN', 3) === 0) {
        $ext_name = str_replace("\\", "/", $ext_name);
    }
    return $ext_name;
}

function check_filename($filename)
{
    $result = true;
    if ($result) {
        $test = array('://', '~', '`', '\'', '"', ':', ';', ',', '&', '>', '<');
        for ($i = 0, $count = count($test); $i < $count && $result; $i++) $result = (strpos($filename, $test[$i]) === false);
    }
    return $result;
}

/**
 * Привидение типа в соответствии с типом поля в КБ
 *
 * Необходима для корректного сравнения изменений и запуска вычислений при изменения полей.
 *
 * @param $value
 * @param int $type
 * @param bool $mult
 *
 * @return float|int|string
 */
function type_conversion($value, $type = 3, $mult = false)
{
    // Если не массив то приводим к нужному типу
    if (!is_array($value) && !$mult) {
        if ($type == 1) {
            // Если поле типа число приводим к типу float
            if (is_infinite((float)$value) || is_nan((float)$value)) {
                $value = 0;
            }
            $value = (float)$value;
        } elseif (($type == 10) OR
            ($type == 5) OR
            ($type == 14) OR
            ($type == 7) OR
            ($type == 11)
        ) {
            // Если поле типа id, связь, группа, пользователь приводим к типу integer
            $value = (integer)$value;
        } elseif ($type == 2) {
            $value = $value === '' ? '0000-00-00 00:00:00' : (string)$value;
        } else {
            // Остальные приводим к строкам
            $value = (string)$value;
        }
    }
    return $value;
}

function sql_functions_replace($text)
{
    return str_replace(
        array(
            'mysql_query(',
            'mysql_insert_id(',
            'mysql_fetch_assoc(',
            'mysql_error(',
            'mysql_close(',
            'mysql_fetch_array(',
            'mysql_fetch_row(',
            'mysql_num_rows(',
            'mysql_numrows(',
            'mysql_free_result(',
            'mysql_real_escape_string(',
            'mysql_escape_string(',
            'mysql_affected_rows('
        ),
        array(
            'sql_query(',
            'sql_insert_id(',
            'sql_fetch_assoc(',
            'sql_error(',
            'sql_close(',
            'sql_fetch_array(',
            'sql_fetch_row(',
            'sql_num_rows(',
            'sql_num_rows(',
            'sql_free_result(',
            'sql_real_escape_string(',
            'sql_real_escape_string(',
            'sql_affected_rows('
        ),
        $text);
}

/**
 * Преобразует hex в rgb
 * @param $color
 * @return string
 */
function hexToRgb($color)
{
    list($red, $green, $blue) = sscanf($color, "#%02x%02x%02x");
    return $color = "$red, $green, $blue";
}

/**
 * @return string
 * генерирует uuid
 */
function gen_uuid()
{
    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        // 32 bits for "time_low"
        mt_rand(0, 0xffff), mt_rand(0, 0xffff),

        // 16 bits for "time_mid"
        mt_rand(0, 0xffff),

        // 16 bits for "time_hi_and_version",
        // four most significant bits holds version number 4
        mt_rand(0, 0x0fff) | 0x4000,

        // 16 bits, 8 bits for "clk_seq_hi_res",
        // 8 bits for "clk_seq_low",
        // two most significant bits holds zero and one for variant DCE1.1
        mt_rand(0, 0x3fff) | 0x8000,

        // 48 bits for "node"
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
    );
}

/**
 * @param $str
 * @return bool
 * Проверяет строку на дату
 */
function is_Date($str)
{
    return is_numeric(strtotime($str));
}

function getDbNameField($field_id, $field_type)
{
    return \CB\CornyService::getInnerFieldName($field_id, $field_type);
}

/**
 * @param $field_info - массив с полями
 * @param $search_text - текст поиска
 * @param $table - id таблицы
 * @param $limit - число записей из одной таблицы
 * @return string
 * Формирует строку для поиска, для числовых =, для строк %like%
 * С учетом полей связи, пользователь, группа
 */

function form_cond($fields_info, $search_text, $table, $limit = '')
{
    $types = array(3, 4, 5, 6, 7, 9, 11, 14);
    if (is_Date($search_text)) {
        $types[] = 2;
        $types[] = 12;
    }
    if (is_numeric($search_text)) {
        $types[] = 1;
        $types[] = 10;
    }
    if ($limit !== '') {
        $limit = ' LIMIT ' . $limit;
    }
    $select = 'SELECT ' . DATA_TABLE . $table . '.*';
    $from = ' FROM ' . DATA_TABLE . $table . ' ';
    $where = ' WHERE ';
    $info = array('join', 'where', 'select');
    foreach ($fields_info as $field_info) {
        if (in_array($field_info['type_field'], $types)) {
            if (!test_allow_read($field_info, '', 'view') && !test_allow_read($field_info, '', 'view_tb ')) {
                continue;
            }
            $info['field'] = $field_info;
            form_cond_field($field_info, $search_text, $info);
        } else {
            continue;
        }
    }
    if (!count($info['where'])) {
        return false;
    }

    $join = [];
    foreach ($info['join'] as $one_pos) {
        $key = explode(' ON ', $one_pos)[0];
        if (!$join[$key]) {
            $join[$key] = $one_pos;
        }
    }

    if ($imp_cond = implode(', ', $info['select'])) {
        $select .= ', ' . $imp_cond;
    }

    $cond = $select . $from . implode(' ', $join) . $where . implode(' OR ', $info['where']) . $limit;
    return $cond;
}

function form_cond_link($field_info, $search_text, &$info = array(), $recoursive = array())
{
    $type_value = explode('|', $field_info['type_value']);
    $link_table_id = (int)$type_value[0];
    $link_field_id = (int)$type_value[1];
    $result = sql_query('SELECT * FROM ' . FIELDS_TABLE . ' WHERE id = ' . $link_field_id);
    if ($row = sql_fetch_assoc($result)) {
        if ($field_info['alias_table']) {
            $before_table = $field_info['alias_table'];
        } else {
            $before_table = DATA_TABLE . $field_info['table_id'];
        }
        $alias = ' AS ' . DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $alias_table = DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $recoursive['join'][] = 'LEFT JOIN ' . DATA_TABLE . $link_table_id . $alias . ' ON '
            . $before_table . '.' . getDbNameField($field_info['id'], $field_info['type_field']) . ' = '
            . $alias_table . '.id';

        if ($row['type_field'] != 5 && $row['type_field'] != 7 && $row['type_field'] != 11 && $row['type_field'] != 14) {
            if (is_Date($search_text) && ($row['type_field'] == 2 || $row['type_field'] == 12)) {
                $row['alias_table'] = $alias_table;
                if ($info['join']) {
                    $info['join'] = array_merge($info['join'], $recoursive['join']);
                } else {
                    $info['join'] = $recoursive['join'];
                }
                form_cond_field($row, $search_text, $info);
                $info['select'][] = $alias_table . '.' . getDbNameField($row['id'], $row['type_field']) . ' AS _' . getDbNameField($info['field']['id'], $info['field']['type_field']);
            } else if (is_numeric($search_text) && ($row['type_field'] == 1 || $row['type_field'] == 10)) {
                $row['alias_table'] = $alias_table;
                if ($info['join']) {
                    $info['join'] = array_merge($info['join'], $recoursive['join']);
                } else {
                    $info['join'] = $recoursive['join'];
                }
                form_cond_field($row, $search_text, $info);
                $info['select'][] = $alias_table . '.' . getDbNameField($row['id'], $row['type_field']) . ' AS _' . getDbNameField($info['field']['id'], $info['field']['type_field']);
            } else if ($row['type_field'] == 3 || $row['type_field'] == 4 || $row['type_field'] == 6 || $row['type_field'] == 9) {
                $row['alias_table'] = $alias_table;
                if ($info['join']) {
                    $info['join'] = array_merge($info['join'], $recoursive['join']);
                } else {
                    $info['join'] = $recoursive['join'];
                }
                form_cond_field($row, $search_text, $info);
                $info['select'][] = $alias_table . '.' . getDbNameField($row['id'], $row['type_field']) . ' AS _' . getDbNameField($info['field']['id'], $info['field']['type_field']);
            }
        } else {
            if ($row['type_field'] == 5) {
                $row['alias_table'] = $alias_table;
                return form_cond_link($row, $search_text, $info, $recoursive);
            }
            if ($row['type_field'] == 7 || $row['type_field'] == 11) {
                $info['join'] = array_merge($info['join'], $recoursive['join']);
                $row['alias_table'] = $alias_table;
                return form_cond_user($row, $search_text, $info);
            }
            if ($row['type_field'] == 14) {
                $info['join'] = array_merge($info['join'], $recoursive['join']);
                $row['alias_table'] = $alias_table;
                return form_cond_group($row, $search_text, $info);
            }
        }
        return $info;
    }
}

function form_cond_user($field_info, $search_text, &$info = array())
{
    if ($field_info['type_field'] == 7 || $field_info['type_field'] == 11) {
        if ($field_info['alias_table']) {
            $before_table = $field_info['alias_table'];
        } else {
            $before_table = DATA_TABLE . $field_info['table_id'];
        }
        $alias = ' AS ' . DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $alias_table = DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $info['join'][] = 'LEFT JOIN ' . USERS_TABLE . $alias . ' ON ' . $before_table . '.' . getDbNameField($field_info['id'], $field_info['type_field']) . ' = ' . $alias_table . '.id';
        $info['select'][] = $alias_table . '.fio AS _' . getDbNameField($info['field']['id'], $info['field']['type_field']);
        $info['where'][] = $alias_table . '.fio LIKE "%' . $search_text . '%"';
        return $info;
    }
    return 'invalid type';
}

function form_cond_group($field_info, $search_text, &$info = array())
{
    if ($field_info['type_field'] == 14) {
        if ($field_info['alias_table']) {
            $before_table = $field_info['alias_table'];
        } else {
            $before_table = DATA_TABLE . $field_info['table_id'];
        }
        $alias = ' AS ' . DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $alias_table = DATA_TABLE . $field_info['table_id'] . '_' . getDbNameField($field_info['id'], $field_info['type_field']);
        $info['join'][] = 'LEFT JOIN ' . GROUPS_TABLE . $alias . ' ON ' . $before_table . '.' . getDbNameField($field_info['id'], $field_info['type_field']) . ' = ' . $alias_table . '.id';
        $info['select'][] = $alias_table . '.name AS _' . getDbNameField($info['field']['id'], $info['field']['type_field']);
        $info['where'][] = $alias_table . '.name LIKE "%' . $search_text . '%"';
        return $info;
    }
    return 'invalid type';
}

function form_cond_field($field_info, $search_text, &$info = array())
{
    $type = $field_info['type_field'];
    if ($field_info['alias_table']) {
        $table_join = $field_info['alias_table'] . '.';
    } else {
        $table_join = DATA_TABLE . $field_info['table_id'] . '.';
    }
    switch ($type) {
        case 1:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' = ' . $search_text;
            break;
        case 2:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 3:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 4:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 5:
            form_cond_link($field_info, $search_text, $info);
            break;
        case 6:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 7:
            form_cond_user($field_info, $search_text, $info);
            break;
        case 9:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 10:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' = ' . $search_text;
            break;
        case 11:
            form_cond_user($field_info, $search_text, $info);
            break;
        case 12:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' LIKE "%' . $search_text . '%"';
            break;
        case 13:
            $info['where'][] = $table_join . getDbNameField($field_info['id'], $type) . ' = ' . $search_text;
            break;
        case 14:
            form_cond_group($field_info, $search_text, $info);
            break;
    }
}

/**
 * Функция для отправки команд
 *
 * @param $command
 * @param $url
 * @param int $timeout Количество миллисекунд для ожидания ответа от сервиса
 * @return mixed
 */
function send_command($command, $url, $timeout = null)
{
    global $config;

    // Пеобразуем в json
    $data_string = json_encode($command);

    // установка URL и других необходимых параметров
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_COOKIE, "XDEBUG_SESSION=PHPSTORM");
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json',
            'Content-Length: ' . strlen($data_string)
        )
    );

    $tOut = (int)$timeout;
    if ($tOut > 0) {
        curl_setopt($ch, CURLOPT_TIMEOUT_MS, $tOut);
    }

    if (isset($config['proxy_host'])) {
        curl_setopt($ch, CURLOPT_PROXY, $config['proxy_host'] . ($config['proxy_port'] ? ':' . $config['proxy_port'] : ''));
    }
    if (isset($config['proxy_user'])) {
        curl_setopt($ch, CURLOPT_PROXYUSERPWD, $config['proxy_user'] . ':' . $config['proxy_pass']);
    }

    // загрузка страницы и выдача её браузеру
    $json = curl_exec($ch);

    // Переобразуем в массив
    $result = json_decode($json, true);

    // завершение сеанса и освобождение ресурсов
    curl_close($ch);

    if (is_array($result) && count($result)) {
        return $result;
    }

    return $json;
}

/**
 * Функция для получения параметров из URL
 * @param $url
 * @return mixed
 */
function get_url_params($url)
{
    if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
        return false;
    }

    $parse_url = parse_url($url);
    if (isset($parse_url['query'])) {
        $params = array();
        parse_str($parse_url['query'], $params);
        return $params;
    }

    return false;
}

function check_runtime_exists()
{
    global $config, $lang;
    $file_path_json = $config['site_path'] . '/runtime/main.json';
    if (!file_exists($file_path_json) || file_get_contents($file_path_json) === '') {
        $fp = fopen($file_path_json, 'wb+');
        if (flock($fp, LOCK_EX)) { // выполняем эксклюзивную блокировку
            $time = time();
            $run_main = array(
                'head_tables'      => $time,
                'head_reports'     => $time,
                'acc_reports'      => $time,
                'head_calendars'   => $time,
                'categories'       => $time,
                'config'           => $time,
                'sync'             => $time,
                'sync_fields'      => $time,
                'asterisk_servers' => $time,
                'modules'          => $time,
                'crypt'            => $time,
                'key'              => $time
            );
            $string_json = json_encode($run_main, JSON_PRETTY_PRINT);
            fwrite($fp, $string_json);
            fflush($fp); // очищаем вывод перед отменой блокировки
            flock($fp, LOCK_UN); // снимаем блокировку
        } else {
            cb_die($lang['no_block']);
        }
        fclose($fp);
        if (function_exists('opcache_invalidate')) {
            opcache_invalidate($file_path_json, true);
        }
    }
}

function get_runtimes_items()
{
    global $config;
    $file_path_json = $config['site_path'] . '/runtime/main.json';
    check_runtime_exists();
    $run_main = file_get_contents($file_path_json);
    $run_main = json_decode($run_main, true);

    return $run_main;
}

function get_key_db()
{
    $result = sql_select(KEY_TABLE);
    $key = sql_fetch_assoc($result);
    if (isset($key['code'])) {
        return $key['code'];
    } else {
        return '';
    }
}

function get_key()
{
    global $config;
    $run_main = get_runtimes_items();
    $file_path = $config['site_path'] . '/runtime/key.php';
    if ($run_main['key'] + 600 <= time()) {
        update_run_flags('key');
        $string = get_key_db();
        if (file_exists($file_path)) {
            $file_key = file_get_contents($file_path);
        } else {
            $file_key = '';
        }
        if ($string === $file_key) {
            return $file_key;
        } else {
            file_put_contents($file_path, $string);
            return $string;
        }
    }
    if (file_exists($file_path)) {
        $key_return = file_get_contents($file_path);
    } else {
        $key_return = get_key_db();
        file_put_contents($file_path, $key_return);
    }
    return $key_return;
}

function get_color_items()
{
    global $config, $color_scheme;
    $file_path = $config['site_path'] . '/runtime/color_scheme_file.php';
    if (!file_exists($file_path)) {
        $result = sql_select(SCHEMES_TABLE, 'active=1');
        $color_scheme = sql_fetch_assoc($result);
        $color_scheme_file = [
            'id'                => $color_scheme['id'],
            'color1'            => $color_scheme['color1'],
            'color2'            => $color_scheme['color2'],
            'color3'            => $color_scheme['color3'],
            'color4'            => $color_scheme['color4'],
            'color7'            => $color_scheme['color7'],
            'active'            => '1',
            'gradient_stat'     => $color_scheme['gradient_stat'],
            'size_opacity'      => $color_scheme['size_opacity'],
            'img_select'        => $color_scheme['img_select'],
            'second_img_select' => $color_scheme['second_img_select'],
            'table_bg'          => $color_scheme['table_bg'],
            'radius_menu'       => $color_scheme['radius_menu'],
            'font_select'       => $color_scheme['font_select']
        ];
        $string = "<?php\n" . '$color_scheme' . " = " . var_export($color_scheme_file, true) . ';';
        file_put_contents($file_path, $string);
    }
    include $file_path;

    return $color_scheme;
}

function get_crypt()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['crypt']) && count($_SESSION[$ses_id]['crypt']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['crypt'] >= $_SESSION[$ses_id]['crypt']['last_time']) {
            unset($_SESSION[$ses_id]['crypt']);
            return get_crypt();
        }
        return $_SESSION[$ses_id]['crypt']['items'];
    }
    $modules = array();
    $result = sql_select(FIELDS_TABLE, "crypt=1");
    while ($row = sql_fetch_assoc($result)) {
        $modules['f' . $row['id']] = 1;
    }
    $_SESSION[$ses_id]['crypt'] = array('items' => $modules, 'last_time' => time());
    return $_SESSION[$ses_id]['crypt']['items'];
}

function get_modules()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['modules']) && count($_SESSION[$ses_id]['modules']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['modules'] >= $_SESSION[$ses_id]['modules']['last_time']) {
            unset($_SESSION[$ses_id]['modules']);
            return get_modules();
        }
        return $_SESSION[$ses_id]['modules']['items'];
    }
    $modules = array();
    $result = sql_select(MODULES_TABLE);
    while ($one_field = sql_fetch_assoc($result)) {
        $modules[] = $one_field;
    }
    $_SESSION[$ses_id]['modules'] = array('items' => $modules, 'last_time' => time());
    return $_SESSION[$ses_id]['modules']['items'];
}

function get_asterisk_servers()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['asterisk_servers']) && count($_SESSION[$ses_id]['asterisk_servers']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['asterisk_servers'] >= $_SESSION[$ses_id]['asterisk_servers']['last_time']) {
            unset($_SESSION[$ses_id]['asterisk_servers']);
            return get_asterisk_servers();
        }
        return $_SESSION[$ses_id]['asterisk_servers']['items'];
    }
    $asterisk_servers = array();
    $result = sql_select(ASTERISK_SERVERS);
    while ($row = sql_fetch_assoc($result)) {
        if ($row['server'] == 'sip.clientbase.ru') {
            $row['server'] = 'sip.' . CLIENTBASE_DOMAIN;
        }
        $asterisk_servers[$row['id']] = $row;
    }
    $_SESSION[$ses_id]['asterisk_servers'] = array('items' => $asterisk_servers, 'last_time' => time());
    return $_SESSION[$ses_id]['asterisk_servers']['items'];
}

function get_sync_fields()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['sync_exp_fields']) && count($_SESSION[$ses_id]['sync_exp_fields']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['sync_fields'] >= $_SESSION[$ses_id]['sync_exp_fields']['last_time']) {
            unset($_SESSION[$ses_id]['sync_exp_fields']);
            return get_sync_fields();
        }
        return $_SESSION[$ses_id]['sync_exp_fields']['items'];
    }
    $sync_exp_fields_cache = array();
    $result = sql_query("SELECT a.table_id, b.* FROM " . FIELDS_TABLE . " a, " . SYNC_FIELDS_TABLE . " b WHERE a.id=b.field_id and b.enabled='1'");
    while ($one_field = sql_fetch_assoc($result)) {
        $sync_exp_fields_cache[] = $one_field;
    }
    $_SESSION[$ses_id]['sync_exp_fields'] = array('items' => $sync_exp_fields_cache, 'last_time' => time());
    return $_SESSION[$ses_id]['sync_exp_fields']['items'];
}

function get_sync()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['sync_exp_list']) && count($_SESSION[$ses_id]['sync_exp_list']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['sync'] >= $_SESSION[$ses_id]['sync_exp_list']['last_time']) {
            unset($_SESSION[$ses_id]['sync_exp_list']);
            return get_sync();
        }
        return $_SESSION[$ses_id]['sync_exp_list']['items'];
    }
    $sync_exp_list = array();
    $result = sql_select(SYNC_TABLE, 'enabled=1');
    while ($one_sync = sql_fetch_assoc($result)) {
        $one_sync['tables'] = array();
        $sync_exp_list[$one_sync['id']] = $one_sync;
    }
    $_SESSION[$ses_id]['sync_exp_list'] = array('items' => $sync_exp_list, 'last_time' => time());
    return $_SESSION[$ses_id]['sync_exp_list']['items'];
}

function get_config()
{
    global $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['config_cache']) && count($_SESSION[$ses_id]['config_cache']) > 0) {
        $run_main = get_runtimes_items();
        $support_val = sql_fetch_assoc(sql_select_field(CONFIG_TABLE, "value", "name='techsupport_tickets'"));
        $_SESSION[$ses_id]['config_cache']['techsupport_tickets'] = $support_val['value'];
        if ($run_main['config'] >= $_SESSION[$ses_id]['config_cache']['last_time']) {
            unset($_SESSION[$ses_id]['config_cache']);
            return get_config();
        }
        return $_SESSION[$ses_id]['config_cache'];
    }
    $result = sql_select(CONFIG_TABLE);
    while ($row = sql_fetch_assoc($result)) {
        $_SESSION[$ses_id]['config_cache'][$row['name']] = $row['value'];
    }
    sql_delete(CONFIG_TABLE, "name='first_login'");
    $_SESSION[$ses_id]['config_cache']['last_time'] = time();
    return $_SESSION[$ses_id]['config_cache'];
}

function get_categories()
{
    global $user, $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['categories']) && count($_SESSION[$ses_id]['categories']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['categories'] >= $_SESSION[$ses_id]['categories']['last_time']) {
            unset($_SESSION[$ses_id]['categories']);
            return get_categories();
        }
        return $_SESSION[$ses_id]['categories']['items'];
    }
    $cats = array();
    $result = sql_query('SELECT a.name, id, a.icon, a.icon2, a.show_text FROM ' . CATS_TABLE . ' a, ' . ACC_CATS_TABLE . ' b WHERE a.id=b.cat_id AND group_id=' . $user['group_id'] . ' AND b.access=1 ORDER BY num');
    while ($row = sql_fetch_assoc($result)) {
        $cats[$row['id']] = $row;
    }
    $_SESSION[$ses_id]['categories'] = array('items' => $cats, 'last_time' => time());
    return $_SESSION[$ses_id]['categories']['items'];
}

function get_head_tables($cats)
{
    global $user, $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['head_tables']) && count($_SESSION[$ses_id]['head_tables']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['head_tables'] >= $_SESSION[$ses_id]['head_tables']['last_time']) {
            unset($_SESSION[$ses_id]['head_tables']);
            return get_head_tables($cats);
        }
        return $_SESSION[$ses_id]['head_tables']['items'];
    }
    $tables = array();
    $result = sql_query("SELECT a.id, a.name_table, a.table_num, a.cat_id FROM " . TABLES_TABLE . " a, " . ACC_TABLES_TABLE . " b WHERE a.id=b.table_id AND group_id=" . $user['group_id'] . " AND b.acc>0 AND vis_acc=1 AND cat_id IN ($cats) ORDER BY table_num");
    while ($row = sql_fetch_assoc($result)) {
        $tables[$row['id']] = $row;
    }
    $_SESSION[$ses_id]['head_tables'] = array('items' => $tables, 'last_time' => time());
    return $_SESSION[$ses_id]['head_tables']['items'];

}

function get_head_reports($cats)
{
    global $user, $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['head_reports']) && count($_SESSION[$ses_id]['head_reports']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['head_reports'] >= $_SESSION[$ses_id]['head_reports']['last_time']) {
            unset($_SESSION[$ses_id]['head_reports']);
            return get_head_reports($cats);
        }
        return $_SESSION[$ses_id]['head_reports']['items'];
    }
    $reports = array();
    $result = sql_query("SELECT a.id, a.name, a.num, b.access, a.cat_id FROM " . REPORTS_TABLE . " a, " . ACC_REPORTS_TABLE . " b WHERE a.id=b.report_id AND group_id=" . $user['group_id'] . " AND cat_id IN ($cats) AND arc = 0 ORDER BY num");
    while ($row = sql_fetch_assoc($result)) {
        $reports[$row['id']] = $row;
    }
    $_SESSION[$ses_id]['head_reports'] = array('items' => $reports, 'last_time' => time());
    return $_SESSION[$ses_id]['head_reports']['items'];

}

function get_acc_reports()
{
    global $user, $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['head_acc_reports']) && count($_SESSION[$ses_id]['head_acc_reports']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['acc_reports'] >= $_SESSION[$ses_id]['head_acc_reports']['last_time']) {
            unset($_SESSION[$ses_id]['head_acc_reports']);
            return get_acc_reports();
        }
        return $_SESSION[$ses_id]['head_acc_reports']['items'];
    }
    $acc_reports = array();
    $result = sql_query("SELECT report_id FROM " . ACC_REPORTS_USERS_TABLE . " WHERE user_id=" . $user['id']);
    while ($row = sql_fetch_assoc($result)) {
        $acc_reports[$row['report_id']] = 1;
    }
    $_SESSION[$ses_id]['head_acc_reports'] = array('items' => $acc_reports, 'last_time' => time());
    return $_SESSION[$ses_id]['head_acc_reports']['items'];

}

function get_head_calendars($cats)
{
    global $user, $ses_id;
    cb_session_write_close();
    session_start();
    if (isset($_SESSION[$ses_id]['head_calendars']) && count($_SESSION[$ses_id]['head_calendars']['items']) > 0) {
        $run_main = get_runtimes_items();
        if ($run_main['head_calendars'] >= $_SESSION[$ses_id]['head_calendars']['last_time']) {
            unset($_SESSION[$ses_id]['head_calendars']);
            return get_head_calendars($cats);
        }
        return $_SESSION[$ses_id]['head_calendars']['items'];
    }
    $calendars = array();
    $result = sql_query("SELECT a.id, a.name, a.num, a.cat_id, a.cat_id FROM " . CALENDARS_TABLE . " a, " . ACC_CALENDARS_TABLE . " b WHERE a.id=b.calendar_id AND group_id=" . $user['group_id'] . " AND access=1 AND cat_id IN ($cats) AND arc = 0 ORDER BY num");
    while ($row = sql_fetch_assoc($result)) {
        $calendars[$row['id']] = $row;
    }
    $_SESSION[$ses_id]['head_calendars'] = array('items' => $calendars, 'last_time' => time());
    return $_SESSION[$ses_id]['head_calendars']['items'];

}

function update_run_flags($item = null)
{
    global $run_main, $config, $lang;
    $file_path_json = $config['site_path'] . '/runtime/main.json';
    check_runtime_exists();
    $fp = fopen($file_path_json, 'rb+');
    if (flock($fp, LOCK_EX)) { // выполняем эксклюзивную блокировку
        $run_main = fread($fp, filesize($file_path_json));
        $run_main = json_decode($run_main, true);
        if (null !== $run_main) {
            if (isset($run_main[$item])) {
                $run_main[$item] = time() + 10;
                $string = json_encode($run_main, JSON_PRETTY_PRINT);
                @file_put_contents($file_path_json, $string);
            } else {
                foreach ($run_main as $item_k=>$item_v) {
                    $run_main[$item_k] = time() + 10;
                }
                $string = json_encode($run_main, JSON_PRETTY_PRINT);
                @file_put_contents($file_path_json, $string);
            }
        }
        fflush($fp); // очищаем вывод перед отменой блокировки
        flock($fp, LOCK_UN); // снимаем блокировку
        fclose($fp);
    } else {
        cb_die($lang['no_block']);
    }
    $get_function = 'get_config';
    if (function_exists($get_function)) {
        $get_function();
    }
}

function cb_build_info()
{
    if (file_exists('revision')) {
        return file_get_contents('revision');
    }

    return '';
}

function cb_build_revision()
{
    $text = cb_build_info();
    $pos = strrpos($text, 'Revision: ');
    if ($pos !== false) {
        $pos += 10;
        return (int)substr($text, $pos, 5);
    }
    return false;
}

function cb_build_date()
{
    $text = cb_build_info();
    $pos = strrpos($text, 'Last Changed Date: ');
    if ($pos !== false) {
        $pos += 19;
        return substr($text, $pos, 19);
    }
    return '';
}

/**
 * Загрузить данные о подтаблице.
 *
 * @param int $subtable_id          Идентификатор подтаблицы
 * @return array                    Разнообразное кол-во данных обо всем (данные повторяются в подмассивах)
 */
function subtables_load($subtable_id): array
{
    global $user, $config, $main_table_id, $main_line_id, $subtable_rights, $full_version_obfuscated, $ASTERISK_SETTINGS;

    $start_time = microtime(true);

    $main_line = data_select_array($main_table_id, 'id = ', $main_line_id);
    $subtable = sql_select_array(SUBTABLES_TABLE, "id = ", $subtable_id);
    $name_subtable = $subtable['name'];
    $link_table_id = $subtable['link_table_id'];
    $link_field_id = $subtable['link_field_id'];
    $st_filter_id = $subtable['filter_id'];
    $autostatus = $subtable['autostatus_calc_id'] ? 1 : 0;
    $current_group_field_value = false;
    $current_group_field_pp = 0;
    $groups_fields = array(); // массив содержит список строк, в данной группе
    $groups_fields_defs = array(); // массив содержит значения по умлочанию по группированному полю
    $line_pp = 0;
    $one_table = get_table($link_table_id);
    $sel_fields = get_table_fields($one_table);
    $link_field = $sel_fields[$link_field_id];
    if ($link_field['mult_value']) {
        $link_search = "(" . $link_field['int_name'] . " = " . $main_line['id'] . " OR " . $link_field['int_name'] . " LIKE '%-" . $main_line['id'] . "-%')";
    } else {
        $link_search = $link_field['int_name'] . " = " . $main_line_id;
    }

    // читаем права доступа
    $rights = sql_select_array(ACC_SUBTABLES_TABLE, 'subtable_id = ', $subtable_id, ' AND group_id = ', $user['group_id']);
    $one_table['access'] = $rights['read_acc'];
    $one_table['add'] = $rights['add_acc'];
    $one_table['del'] = $rights['del_acc'];
    $one_table['jump'] = $rights['jump_acc'];
    $result = sql_select(ACC_RULES_TABLE, 'table_id = ', $main_table_id, ' AND (group_id = ', $user['group_id'], ' OR global = 1) AND disabled = 0 ORDER BY num');
    while ($row = sql_fetch_assoc($result)) {
        $cond_php = str_replace('$line', '$main_line', $row['condition_php']);
        if (eval('return ' . $cond_php . ';')) {
            $rights = unserialize($row['rights']);
            foreach ($rights as $one_right) {
                if ($one_right['subtable'] == $subtable_id) {
                    $one_table['access'] = $one_right['read_acc'] ?? $one_table['access'];
                    $one_table['add'] = $one_right['add_acc'] ?? $one_table['add'];
                    $one_table['del'] = $one_right['del_acc'] ?? $one_table['del'];
                    $one_table['jump'] = $one_right['jump_acc'] ?? $one_table['jump'];
                }
            }
        }
    }

    if (!$one_table['view_sub']) { // дополнительная проверка на правила по подтаблице
        $result = sql_select(ACC_SUBTABLE_RULES_TABLE, 'subtable_id = ', $subtable_id, ' AND (group_id = ', $user['group_id'], ' OR group_id = 0) AND access = 1');
        if (sql_num_rows($result)) {
            $one_table['view_sub'] = 1;
        }
    }

    if (!$one_table['access'] || !$one_table['view_sub']) {
        return []; // нет доступа к подтаблице
    }

    // Формируем сводную таблицу вычислений, которые связаны с текущей таблицей
    // Вызываем вычисления о отображении напрямую, без вызова popup_event
    // таким образом избегаем двойного срабатывания вычисления, если стоит условие на показа у двух полей
    $calcs = array();
    $sqlQuery = "SELECT a.type as cond_type, a.param as cond_param, a.param2 as cond_param2, b.* FROM " . CALC_COND_TABLE . " a, " . CALC_TABLE . " b WHERE a.type=2 and a.calc_id=b.id and b.table_id='$link_table_id' and b.disabled=0 ORDER BY num, id";
    $result = sql_query($sqlQuery);
    while ($cond_l = sql_fetch_assoc($result)) {
        if (!$calcs[$cond_l['id']]) { // если вычисление, не заполнено в массив, добавляем его.
            $calc['id'] = $cond_l['id'];
            $calc['table_id'] = $cond_l['table_id'];
            $calc['calculate'] = $cond_l['calculate'];
            $calc['old_format'] = $cond_l['old_format'];
            $calc['link_fields'] = unserialize($cond_l['link_fields']);
            $calc['recursion_disabled'] = $cond_l['recursion_disabled'];
            $calcs[$calc['id']] = $calc;
        }

        $cond['type'] = $cond_l['cond_type'];
        $cond['param'] = $cond_l['cond_param'];
        $cond['param2'] = $cond_l['cond_param2'];
        $calcs[$cond_l['id']]['conditions'][] = $cond;
    };
    $one_table['clacs'] = $calcs;

    $all_lines = array();
    $display_fields_count = 1;
    foreach ($sel_fields as $k => $one_field) { // Определяем по умолчанию видимые столбцы
        $sel_fields[$one_field['id']]['hidden_tbl'] = 1;
        if ($one_field['view_sub']) {
            $sel_fields[$one_field['id']]['hidden_tbl'] = 0;
            if (!$one_field["group_field"]) {
                $display_fields_count++;
            }
        }
        $sel_fields[$k]['prefix'] = form_bbcode_display($one_field['prefix']);
        $sel_fields[$k]['postfix'] = form_bbcode_display($one_field['postfix']);
    };

    // Дополняем показываемые поля, из правил
    if ($one_table['rules']) {
        foreach ($one_table['rules'] as $one_rule) { // Перебираем правила, смотрим, есть ли доступ к полю
            foreach ($one_rule['rights'] as $key => $one_right_group) {
                if ($key <= 0) {
                    continue;
                }
                foreach ($one_right_group as $one_right) {
                    if ($one_right['field'] and $one_right['view_sub']) {
                        $sel_fields[$one_right['field']]['hidden_tbl'] = 0;
                        if (!$sel_fields[$one_right['field']]["group_field"]) {
                            $display_fields_count++;
                        }
                    }
                }
            }
        }
    }

    // Правило для текущей подтаблицы
    $result = sql_select(ACC_SUBTABLE_RULES_TABLE, 'subtable_id = ', $subtable_id, ' AND (group_id = ', $user['group_id'], ' OR group_id = 0)');
    while ($row = sql_fetch_assoc($result)) {
        $subtable_rights[$row['field_id']] = $row['access'];
        if (isset($sel_fields[$row['field_id']])) {
            $sel_fields[$row['field_id']]['hidden_tbl'] = $row['access'] ? 0 : 1;
        }
    }

    // Права на добавление значений в поле связи
    $link_def_array = array();
    $link_def_res = sql_select(LINK_FIELDS_FOR_ADDITION_TABLE, "1=1");
    while ($link_def_row = sql_fetch_assoc($link_def_res)) {
        $link_def_array[$link_def_row['field_id']][$link_def_row['addition_field']] = true;
    }

    foreach ($sel_fields as $k => $subtable_field) {
        $add_link_fields = array();
        $add_link_field_ids = '';
        if ($subtable_field['type_field'] == 5) {
            $table_id_link = $subtable_field['s_table_id'];
            $lnk_table = get_table($table_id_link);
            $add_acc_link_value = $lnk_table['add'];
            $add_link_text = $lnk_table['add_text'];
            if (!$full_version_obfuscated) {
                $result = data_select_field($lnk_table['id'], 'count(*) as total_rec');
                $row = sql_fetch_assoc($result);
                if ($row['total_rec'] >= MAX_TABLE_RECORDS) {
                    $lnk_tbl_lim_alert = 1;
                } else {
                    $lnk_tbl_lim_alert = 0;
                }
            }

            $lnk_fields = get_table_fields($lnk_table);
            $def_line = array(); // Строка по умолчанию
            foreach ($lnk_fields as $l_field) {
                $def_line[$l_field['int_name']] = default_tpl_replace($l_field['default_value'], $l_field['id'], $l_field['mult_value']);
            }

            $def_line['id'] = $subtable_field['id'];
            $l_field = $lnk_fields[$subtable_field['s_field_id']];
            if ($l_field['type_field'] != 6 && $l_field['type_field'] != 9) { // Не работает с полями типа файл
                foreach ($lnk_fields as $l_field) {
                    if ($l_field['int_name'] == 'id') {
                        $work_field = 1;
                    } else {
                        $work_field = 0;
                    }

                    $link_def_found = false;
                    if ($link_def_array[$subtable_field['id']][$l_field['id']]) {
                        $link_def_found = true;
                    } //проверяем наличие поля среди дополнительных

                    if (((($l_field['main'] || $link_def_found) && (($l_field['view_add'] || $l_field['write'] || test_allow_write($l_field, $def_line)) && !$work_field) && // Поле обязательное, есть права на просмотр
                        ($subtable_field['s_field_filter_id'] != $l_field['id'])) ||  // Вычеркиваются фильтруемые поля
                        ($subtable_field['s_field_id'] == $l_field['id']))) // Поле на которое указывает сама связь
                    {
                        if (test_allow_write($l_field, $def_line, '', 1)) {
                            $l_field['type_out'] = 'write';
                            $one_value = array();
                            form_fast_edit_value($l_field, $def_line, $one_value, 'add_link_field');
                            $l_field['fast_edit_div'] = $one_value['fast_edit_div'];
                            $l_field['fast_edit_div_close'] = $one_value['fast_edit_div_close'];

                            if (in_array($l_field['type_field'], [2, 4, 5, 7, 11, 12, 14])) {
                                $l_field['def_value'] = $one_value['value'];
                            }

                            $add_link_fields[] = $l_field;
                            $add_link_field_ids .= $l_field['id'] . ',';
                        }
                    }
                }
            }

            if (!$add_link_fields) {
                $add_acc_link_value = 0; // если нет редактируемых полей, то запрещаем добавление
            }
        } else {
            $first_edit_field = $GLOBALS['first_edit_field'] ?? 0;
            // разрешено чтение запись, то метим его как первое редактируемое поле
            if ($subtable_field['write'] && $subtable_field['read'] && !$subtable_field['group_field'] && $first_edit_field == -1) {
                $first_edit_field = $subtable_field['id'];
            }
        }

        $sel_fields[$k]['table_id_link'] = $table_id_link;
        $sel_fields[$k]['add_acc_link_value'] = $add_acc_link_value;
        $sel_fields[$k]['lnk_tbl_lim_alert'] = $lnk_tbl_lim_alert;
        $sel_fields[$k]['add_link_text'] = $add_link_text;
        $sel_fields[$k]['add_link_fields'] = $add_link_fields;
        $sel_fields[$k]['add_link_field_ids'] = $add_link_field_ids;
    }

    // Фильтр в подчиненной таблице
    $filter = "";
    if ($st_filter_id) { // в свойствах подтаблицы выбран конкретный фильтр
        $sqlQuery = "SELECT a.* FROM " . FILTERS_TABLE . " a, " . ACC_FILTERS_TABLE . " b WHERE a.id=b.filter_id AND b.group_id=" . $user['group_id'] . " AND b.filter_id=$st_filter_id AND (access=1 OR def_use=1)";
        $result = sql_query($sqlQuery);
        if ($row = sql_fetch_assoc($result)) { # фильтр найден и доступен пользователю
            $filter = filter_tpl_replace($row['value'], $row['calculate']);
            $filter_cond_set = unserialize($row['cond_set']) ?: array();
            if ($row['def_sort']) {
                $one_table['def_sort'] = $row['def_sort'];
            }
            if ($row['group_field']) {
                $one_table['group_field'] = $row['group_field'];
                $one_table['desc_group'] = $row['desc_group'];
            }
        }
    }

    if (!$st_filter_id) { // фильтр не выбран или недоступен
        $sqlQuery = "SELECT a.* FROM " . FILTERS_TABLE . " a, " . ACC_FILTERS_TABLE . " b WHERE a.id=b.filter_id AND b.group_id=" . $user['group_id'] . " AND b.table_id=$link_table_id AND (access=1 OR def_use=1)";
        $result = sql_query($sqlQuery);
        while ($row = sql_fetch_assoc($result)) { # объединяем все доступные пользователю фильтры, т.к. в подтаблице нет возможности их переключать
            $st_filter_id = $row['id'];
            $row['value'] = filter_tpl_replace($row['value'], $row['calculate']);
            $filter_cond_set = unserialize($row['cond_set']) ?: array();
            if ($row['value']) {
                $filter .= "(" . $row['value'] . ") OR ";
            } else { // Все записи
                $filter = "";
                break;
            }
        }

        if ($filter) {
            $filter = substr($filter, 0, -4);
        }
    }

    if ($filter) {
        $filter = " AND (" . $filter . ")";
    }

    if ($one_table['rule_filter']) {
        $filter .= " AND (" . filter_tpl_replace($one_table['rule_filter']) . ")";
    }

    $filter_def_values = array();
    $filter_hidden_fields = array();
    if (sql_num_rows($result) == 1) { // Если фильтр один, то считываем из его настроек значения по умолчанию и флажки скрытия полей
        $filter_defaults = array();
        foreach ($filter_cond_set as $one_cond) {
            if ($one_cond['term'] == '=' && isset($sel_fields[$one_cond['field']])) {
                if ($sel_fields[$one_cond['field']]['type_field'] != 2 && $sel_fields[$one_cond['field']]['type_field'] != 12) {
                    $filter_defaults[$one_cond['field']][] = $one_cond['value'];
                }
            }
        }

        foreach ($filter_defaults as $field_id => $values) {
            if (count($values) == 1) {
                $filter_def_values[$field_id] = $values[0];
            }
        }

        $result = sql_select_field(FILTER_FIELDS_TABLE, "field_id, def_value, status", "filter_id=", $st_filter_id);
        while ($row = sql_fetch_assoc($result)) {
            if ($row['def_value'] != null) {
                $filter_def_values[$row['field_id']] = $row['def_value'];
            }
            $filter_hidden_fields[$row['field_id']] = $row['status'];
        }
    }

    $one_table['add'] = test_allow_right($one_table, $def_line, 'add');

    if ($one_table['add']) {
        $add_any_subtbl_field = 0;
        foreach ($sel_fields as $one_field) {
            if (test_allow_write($one_field, $def_line, "view_add")) {
                $add_any_subtbl_field = 1;
            }
        }
        $one_table['add'] = $add_any_subtbl_field;
    }

    // Правила сортировки
    $sort_rule = " ORDER BY ";
    if ($one_table['group_field']) {
        $sort_rule .= $sel_fields[$one_table['group_field']]['int_name'] . ($one_table['desc_group'] ? " DESC" : "") . ", ";
    }

    if ($one_table['def_sort']) {
        $sort_rule .= str_replace("`", "", $one_table['def_sort']) . ",";
    }

    $sort_rule .= " id";

    // Количество строк на страницу
    if ($one_table["SLOP"]) {
        if ($_REQUEST['subtable_page']) {
            $subtable_page = $_REQUEST['subtable_page'];
        } else {
            $subtable_page = 1;
        }

        $start_limit = ($subtable_page - 1) * $one_table["SLOP"];
        $limit_rule = " LIMIT " . $one_table["SLOP"] . " OFFSET $start_limit";

        // Страницы
        $result = data_select_field($one_table['id'], 'COUNT(*) AS cnt',
            'status = ' . ($autostatus ? $main_line['status'] : ($main_line['status'] < 3 ? 0 : 3)) . ' AND ' . $link_search . $filter);
        $row = sql_fetch_assoc($result);
        $all_cnt_pages = $row['cnt'];
        $cnt_pages = ceil($row['cnt'] / $one_table["SLOP"]);
        $subtbl_pages = array();

        if ($cnt_pages > 1) {
            if (!$config['max_pages_count']) {
                $config['max_pages_count'] = 15;
            }
            if ($cnt_pages < $config['max_pages_count']) { // меньше 15 страниц выводим просто так
                for ($i = 0; $i < $cnt_pages; $i++) {
                    $subtbl_pages[] = $i + 1;
                }
            } else { // больше 15 страниц группируем
                $mdl = (int)($config['max_pages_count'] / 2);
                $st_i = $subtable_page - $mdl;
                if ($st_i < 0) {
                    $end_plus = -$st_i;
                }
                $end_i = $subtable_page + (int)($config['max_pages_count'] / 2) + $end_plus;
                if ($end_i > $cnt_pages) {
                    $st_i = $st_i - ($end_i - $cnt_pages);
                }
                // ограничители
                if ($st_i < 0) {
                    $st_i = 0;
                }
                if ($end_i > $cnt_pages) {
                    $end_i = $cnt_pages;
                }
                if ($st_i > 0) {
                    $subtbl_pages[] = 1;
                    if ($st_i > 1) {
                        $subtbl_pages[] = "...";
                    }
                }
                for ($i = $st_i; $i < $end_i; $i++) {
                    $subtbl_pages[] = $i + 1;
                }
                if ($end_i < $cnt_pages) {
                    if ($end_i < ($cnt_pages - 1)) {
                        $subtbl_pages[] = "...";
                    }
                    $subtbl_pages[] = $cnt_pages;
                }
            }
        }

        $one_table['all_cnt_pages'] = $all_cnt_pages;
        $one_table['pages'] = $subtbl_pages;
        $one_table['cur_page'] = $subtable_page;
    } else {
        $limit_rule = "";
    }

    // Вывод существующих записей подтаблицы
    $result = data_select($one_table['id'],
        'status = ' . ($autostatus ? $main_line['status'] : ($main_line['status'] < 3 ? 0 : 3)) . ' AND ' . $link_search . $filter . $sort_rule . $limit_rule);
    while ($line = sql_fetch_assoc($result)) {
        $one_line = array();
        $show_any_field = 0;
        // служебный столбец
        $one_value["line_id"] = $line['id'];
        $one_value["field_id"] = 0;
        $one_value["group_field_text"] = "";
        $one_value["line_pp"] = $line_pp;
        $one_value["can_del"] = test_allow_right($one_table, $line, "del");
        if ($one_table["group_field"]) {
            $value = $line[form_int_name($one_table["group_field"])];
            if ($current_group_field_value !== $value) { // изменилось поле группировки, изменяем служебный столбец
                $current_group_field_pp++;
                $current_group_field_value = $value;
                $one_t_field = $sel_fields[$one_table["group_field"]];
                $value_display = form_display_type($one_t_field, $line, "view_sub");
                if ($value_display === false) { // нет доступа
                    $value_display = "<span class='no_access'>" . Core::get(Core::COMMON_CONTEXT)->lang('No_access') . "</span>";
                }

                if ($value_display == "") {
                    $value_display = "&nbsp;";
                }

                $one_value["group_field_text"] = $value_display;
                $one_value["group_field_pp"] = $current_group_field_pp;
            }

            if (!$groups_fields[$current_group_field_pp]) {
                $groups_fields[$current_group_field_pp] = array();
            }

            $groups_fields[$current_group_field_pp][] = $line["id"];
            $groups_fields_defs[$current_group_field_pp] = $value;
        }

        $one_line[0] = $one_value;

        foreach ($sel_fields as $k => $one_field) {
            if ($filter_hidden_fields[$k]) { // поле скрыто через фильтр
                $one_value["hidden_tbl"] = 1;
                $sel_fields[$k]["hidden_tbl"] = 1;
            } else {
                $one_value["type_field"] = $one_field["type_field"];
                $one_value["line_id"] = $line['id'];
                $one_value["field_id"] = $one_field['id'];
                $one_value["line_pp"] = $line_pp;
                $one_value["hidden_tbl"] = $one_field["hidden_tbl"];
                $one_value["group_field"] = $one_field["group_field"];
                $one_value["width"] = $one_field["width"];
                $one_value['prefix'] = $one_field['prefix'];
                $one_value['postfix'] = $one_field['postfix'];
                $one_value['view_html'] = $one_field['view_html'];
                if ($one_value['type_field'] == 2 || $one_value['type_field'] == 12) {
                    $one_value['display_time'] = $one_field['type_value'];
                }

                if (test_allow_read($one_field, $line,
                    "view_sub")) { // Поле будет показано и если на его отображение есть вычисление, выполняем вычисление
                    if ($k != $link_field['id']) {
                        $one_value["hidden_tbl"] = 0;
                        $sel_fields[$k]["hidden_tbl"] = 0;
                    }
                    foreach ($calcs as $one_calc) {
                        $is_start = 0;
                        foreach ($one_calc['conditions'] as $one_cond) {
                            if ($one_cond['param'] == $one_field['id']) {
                                $is_start = 1;
                                break;
                            }
                        }

                        if ($is_start) { // Запускаем вычисление
                            calc_line($one_table, $line, $one_calc, ($event ?? []));
                        }
                    }
                }

                $one_value["value"] = form_display_type($one_field, $line, "view_sub", 0, true);
                if ($one_value["value"] === false) { // нет доступа
                    $one_value["allow_read"] = 0;
                    $one_value["value"] = Core::get(Core::COMMON_CONTEXT)->lang('No_access');
                    $one_value["full_value"] = "";

                    //при работе с подтаблицей, если есть правило, которое не срабатывает
                    //показывается поле и что на него нет доступа, просто скрываем его
                    $one_value["hidden_tbl"] = 1;
                    $sel_fields[$k]["hidden_tbl"] = 1;
                } else { // Определяем что у поля есть строки со значениями
                    $one_value["allow_read"] = 1;
                    if (($one_field["type_field"] == 3) && (!$one_field['view_html'])) {
                        $one_value["value"] = str_replace("<br>", "\n", $one_value["value"]);
                    } else if ($one_field["type_field"] == 7 || $one_field["type_field"] == 14) {
                        $input_value = form_input_type($one_field, $line, 'subtable_user');
                        if ($input_value) {
                            $user_attr = "-";
                            if (!$one_field['mult_value']) {
                                $user_attr .= $input_value['value'] . "-";
                            } else {
                                foreach ($input_value['input'] as $inp_val) {
                                    if ($inp_val['display_value'] == $one_value['value']) {
                                        $user_attr .= $inp_val['list_value'] . "-";
                                    }
                                }
                            }

                            $one_value["value"] = "<span class='subtable__user-not-edit' user-attr='" . $user_attr .
                                "'>" . $one_value["value"] . "</span>";
                        }
                    }

                    $one_value["full_value"] = $one_value["value"];
                    if ($one_value["value"] == $one_value["full_value"]) {
                        $one_value["full_value"] = "";
                    }

                    $show_any_field = 1;
                    if ($one_field['summa']) {
                        $sel_fields[$k]['total'] += $line[$one_field['int_name']];
                        $sel_fields[$k]['totals'][$current_group_field_pp] += $line[$one_field['int_name']];
                    }

                    if ($one_field['average']) {
                        $sel_fields[$k]['average_str'] += $line[$one_field['int_name']];
                        $sel_fields[$k]['my_row_kol']++;
                        $sel_fields[$k]['averages'][$current_group_field_pp] += $line[$one_field['int_name']];
                        $sel_fields[$k]['my_row_kols'][$current_group_field_pp]++;
                    }
                }

                $one_value["allow_write"] = test_allow_write($one_field, $line, "view_edit");
                if ($one_value["allow_write"]) {
                    $show_any_field = 1;
                    $sel_fields[$k]["col_allow_write"] = 1; // хотя бы одно поле редактируемо
                }

                // Формируем поле телефона
                if (!$one_value["allow_write"]) {
                    if ($one_field['phone_type'] && !empty($user['asterisk_login'])) {
                        $hide_phones_for_groups = explode(',', $ASTERISK_SETTINGS['hide_phones_for_groups']);
                        $disp_phones = explode(",", $one_value["value"]);
                        $all_phones = array();

                        foreach ($disp_phones as $p => $disp_phone) {
                            if (in_array($user['group_id'], $hide_phones_for_groups)) {
                                $one_phone = "|" . $p . "|" . $disp_phone;
                            } else {
                                $one_phone = $disp_phone;
                            }

                            $all_phones[$p] = $one_phone;
                        }

                        $one_value["phone_value"] = implode(",", $all_phones);
                    }
                }

                $one_value["fast_edit_div"] = "";
                $one_value["fast_edit_div_full_value"] = "";
                $one_value["fast_edit_div_close"] = "";
                $mob_value = $one_value["value"];

                form_fast_edit_value($one_field, $line, $one_value, "", $subtable_id);

                $one_value["mob_value"] = $mob_value;
                $one_line[$k] = $one_value;
            }
        }

        if ($show_any_field) {
            $all_lines[] = $one_line;
            $line_pp++;
        }
    }

    foreach ($all_lines as $id => $one_line) {
        foreach ($one_line as $k => $one_value) {
            $all_lines[$id][$k]["col_allow_write"] = $sel_fields[$k]["col_allow_write"];
        }
    }

    $total_sum_fields = array();
    $total_avg_fields = array();
    foreach ($sel_fields AS $k => $one_field) {
        if (isset($one_field['total'])) {
            $sel_fields[$k]['total'] = form_local_number($one_field['total'], $one_field['dec_dig']);
            foreach ($one_field['totals'] AS $gr_pp => $gr_value) {
                $sel_fields[$k]['totals'][$gr_pp] = form_local_number($gr_value, $one_field['dec_dig']);
            }
            if ($one_table['pages']) {
                $total_sum_fields[$k] = "SUM(" . $one_field['int_name'] . ") AS sum" . $k;
            }
        }

        if (isset($one_field['average_str'])) {
            if ($sel_fields[$k]['my_row_kol'] > 0) {
                if ($one_field['dec_dig'] > 1) {
                    $sel_fields[$k]['average_str'] = form_local_number($one_field['average_str'] / $sel_fields[$k]['my_row_kol'], $one_field['dec_dig']);
                } else {
                    $sel_fields[$k]['average_str'] = form_local_number($one_field['average_str'] / $sel_fields[$k]['my_row_kol'], 2);
                }
            } else {
                if ($one_field['dec_dig'] > 1) {
                    $sel_fields[$k]['average_str'] = form_local_number($one_field['average_str'], $one_field['dec_dig']);
                } else {
                    $sel_fields[$k]['average_str'] = form_local_number($one_field['average_str'], 2);
                }
            }

            foreach ($one_field['averages'] AS $gr_pp => $gr_value) {
                if ($sel_fields[$k]['my_row_kols'][$gr_pp] > 0) {
                    if ($one_field['dec_dig'] > 1) {
                        $sel_fields[$k]['averages'][$gr_pp] = form_local_number($gr_value / $sel_fields[$k]['my_row_kols'][$gr_pp], $one_field['dec_dig']);
                    } else {
                        $sel_fields[$k]['averages'][$gr_pp] = form_local_number($gr_value / $sel_fields[$k]['my_row_kols'][$gr_pp], 2);
                    }
                } else {
                    if ($one_field['dec_dig'] > 1) {
                        $sel_fields[$k]['averages'][$gr_pp] = form_local_number($gr_value, $one_field['dec_dig']);
                    } else {
                        $sel_fields[$k]['averages'][$gr_pp] = form_local_number($gr_value, 2);
                    }
                }
            }

            if ($one_table['pages']) {
                $total_avg_fields[$k] = "AVG(" . $one_field['int_name'] . ") AS average" . $k;
            }
        }
    }

    if ($total_sum_fields) // Страниц больше одной, считаем все строки
    {
        $sum_res = data_select_field($one_table['id'], implode(", ", $total_sum_fields),
            'status = ' . ($autostatus ? $main_line['status'] : ($main_line['status'] < 3 ? 0 : 3)) . ' AND ' . $link_search . $filter);
        $sum_row = sql_fetch_assoc($sum_res);
        foreach ($sum_row AS $sum_field => $one_sum) {
            $one_field_id = str_replace("sum", "", $sum_field);
            $sel_fields[$one_field_id]['full_total'] = form_local_number($one_sum, $sel_fields[$one_field_id]['dec_dig']);
        }
    }

    if ($total_avg_fields) {
        $avg_res = data_select_field($one_table['id'], implode(", ", $total_avg_fields),
            'status = ' . ($autostatus ? $main_line['status'] : ($main_line['status'] < 3 ? 0 : 3)) . ' AND ' . $link_search . $filter);
        $avg_row = sql_fetch_assoc($avg_res);
        foreach ($avg_row AS $avg_field => $one_avg) {
            $one_field_id = str_replace("average", "", $avg_field);
            if ($sel_fields[$one_field_id]['dec_dig'] > 1) {
                $sel_fields[$one_field_id]['full_average'] = form_local_number($one_avg, $sel_fields[$one_field_id]['dec_dig']);
            } else {
                $sel_fields[$one_field_id]['full_average'] = form_local_number($one_avg, 2);
            }
        }
    }

    // Пустая строка, со значениями по умолчанию
    $line = array('id' => '-1');
    foreach ($sel_fields as $k => $one_field) { // Заполняем строку
        if ($filter_def_values[$one_field['id']]) {
            $value = $filter_def_values[$one_field['id']];
        } else {
            $value = $one_field["default_value"];
        }
        if ($value != '{random}') {
            $value = default_tpl_replace($value);
        }
        if ($one_field["int_name"] != 'id') {
            $line[$one_field["int_name"]] = $value;
        }
        if ($one_field["id"] == $link_field['id']) {
            $line[$one_field["int_name"]] = $main_line_id;
        }
    }

    foreach ($sel_fields as $k => $one_field) {
        if ($filter_hidden_fields[$k]) { // поле скрыто через фильтр
            $one_field["hidden_tbl"] = 1;
        } else { // формируем видимые значения
            $one_value = array();
            $one_value["value"] = form_display_type($one_field, $line, "view_sub");
            if ($one_value["value"] === false) { // нет доступа
                $one_value["value"] = Core::get(Core::COMMON_CONTEXT)->lang('No_access');
                $one_value["allow_read"] = 0;
            } else {
                $one_value["hidden_tbl"] = 0;
                $one_value["allow_read"] = 1;
            }
        }

        $one_value["allow_write"] = test_allow_write($one_field, $line);
        if ($one_value["allow_write"] && $one_value["allow_read"]) {
            form_fast_edit_value($one_field, $line, $one_value, "", $subtable_id);
        }

        $one_value["fast_edit_div"] = "";
        $one_value["fast_edit_div_full_value"] = "";
        $one_value["fast_edit_div_close"] = "";

        if ($one_value["allow_write"] && $one_value["allow_read"]) {
            form_fast_edit_value($one_field, $line, $one_value, "", $subtable_id);
        }

        $one_field["fast_edit_div"] = $one_value["fast_edit_div"];
        $one_field["def_value"] = $one_value["value"];
        $one_field["fast_edit_div_close"] = $one_value["fast_edit_div_close"];

//        if (!$all_lines) { //Указываем возможность редактирования по-умолчанию для добавления новых строк, независимо то того какие значения у существующих строк
            $one_field["col_allow_write"] = $one_value["allow_write"];
            $one_value["col_allow_write"] = $one_value["allow_write"];
//        }

        $sel_fields[$k] = $one_field;
    }

    $configuration_language = Core::get(Core::COMMON_CONTEXT)->lang('conf') ?? [];

    $one_subtable = $one_table;
    $one_subtable['id'] = $subtable_id;
    $one_subtable['lines_count'] = count($all_lines);
    $one_subtable['table_id'] = $link_table_id;
    $one_subtable['name_table'] = $configuration_language[$name_subtable] ?? $name_subtable;
    $one_subtable['rel_table'] = $main_table_id;
    $one_subtable['rel_field'] = $link_field_id;
    $one_subtable['rel_line'] = $main_line_id;
    $one_subtable['first_edit_field'] = $first_edit_field;
    $one_subtable['all_values'] = $all_lines;
    $one_subtable['show_fields'] = $sel_fields;
    $one_subtable['groups_fields'] = $groups_fields;
    $one_subtable['groups_fields_defs'] = json_encode($groups_fields_defs);
    $one_subtable["display_fields_count"] = $display_fields_count * 3;
    $one_subtable["last_group_field_pp"] = $current_group_field_pp;
    $one_subtable['status_line'] = "";
    $one_subtable['filter_id'] = $subtable['filter_id'];
    $one_subtable['url'] = 0;
    $tables_not = '';

    $addon = sql_select(ADDONS_TABLE, 'inactive=1');
    while ($addons = sql_fetch_assoc($addon)) {
        $addon_part = sql_select(ADDON_PARTS_TABLE, 'part_type="table" and addon_id=', $addons['id'], ' and part_id=', $one_table['id']);
        while ($addons_part = sql_fetch_assoc($addon_part)) {
            $tables_not .= ' and link_table_id!=' . $addons_part['part_id'] . ' ';
            $one_subtable['url'] = $addons['url'];
            if (!$addons['url']) {
                $one_subtable['url'] = 'inact';
            }
        }
    }
    if ($autostatus and $main_line['status'] == 1) {
        $one_subtable['status_line'] = "&archive";
    }
    if ($autostatus and $main_line['status'] == 2) {
        $one_subtable['status_line'] = "&deleted";
    }

    if ($one_subtable['auto_entered_field']) {
        $result_auto_entered_field = sql_select_field(CALC_TABLE, 'calculate', "disabled = 0 AND name NOT LIKE 'Button %'");
        while ($row_auto_entered_field = sql_fetch_assoc($result_auto_entered_field)) {
            foreach ($one_subtable['show_fields'] as $sub_field_id => $sub_field_info) {
                $str_to_find = "['" . $sub_field_info['int_name'] . "'] = ";
                if (strripos($row_auto_entered_field['calculate'], $str_to_find) !== false) {
                    $one_subtable['show_fields'][$sub_field_id]['isset_calc'] = 1;
                }
            }
        }
    }

    $end_time = microtime(true);
    if ($config['log_subtables_runtime']) {
        sql_insert(SUBTABLES_LOG_TABLE, ['subtable_id' => $subtable_id, 'date' => date('Y-m-d H:i:s'), 'time' => $end_time - $start_time]);
    }

    return is_array($one_subtable) ? $one_subtable : [];
}

// Получение информации о количестве записей в подтаблице
function get_subtable_num_rows($subtable_id)
{
    global $user, $config, $main_line;

    $subtable = sql_select_array(SUBTABLES_TABLE, "id = ", $subtable_id);
    $link_table_id = $subtable['link_table_id'];
    $link_field_id = $subtable['link_field_id'];
    $st_filter_id = $subtable['filter_id'];
    $autostatus = $subtable['autostatus_calc_id'] ? 1 : 0;
    $one_table = get_table($link_table_id);
    $sel_fields = get_table_fields($one_table);
    $link_field = $sel_fields[$link_field_id];
    if ($link_field['mult_value']) {
        $link_search = "(" . $link_field['int_name'] . " = " . $main_line['id'] . " OR " . $link_field['int_name'] . " LIKE '%-" . $main_line['id'] . "-%')";
    } else {
        $link_search = $link_field['int_name'] . " = " . $main_line['id'];
    }

    // Собираем фильтр
    $filter = "";
    if ($st_filter_id) { # в свойствах подтаблицы выбран конкретный фильтр
        $sqlQuery = "SELECT a.* FROM " . FILTERS_TABLE . " a, " . ACC_FILTERS_TABLE . " b WHERE a.id=b.filter_id AND b.group_id=" . $user['group_id'] . " AND b.filter_id=$st_filter_id AND (access=1 OR def_use=1)";
        $result = sql_query($sqlQuery);
        if ($row = sql_fetch_assoc($result)) { # фильтр найден и доступен пользователю
            $filter = filter_tpl_replace($row['value'], $row['calculate']);
            $filter_cond_set = unserialize($row['cond_set']) ?: array();
        }
    }
    if (!$st_filter_id) { # фильтр не выбран или недоступен
        $sqlQuery = "SELECT a.* FROM " . FILTERS_TABLE . " a, " . ACC_FILTERS_TABLE . " b WHERE a.id=b.filter_id AND b.group_id=" . $user['group_id'] . " AND b.table_id=$link_table_id AND (access=1 OR def_use=1)";
        $result = sql_query($sqlQuery);
        while ($row = sql_fetch_assoc($result)) { # объединяем все доступные пользователю фильтры, т.к. в подтаблице нет возможности их переключать
            $st_filter_id = $row['id'];
            $row['value'] = filter_tpl_replace($row['value'], $row['calculate']);
            $filter_cond_set = unserialize($row['cond_set']) ?: array();
            if ($row['value']) {
                $filter .= "(" . $row['value'] . ") OR ";
            } else { // Все записи
                $filter = "";
                break;
            }
        }
        if ($filter) {
            $filter = substr($filter, 0, -4);
        }
    }
    if ($filter) {
        $filter = " AND (" . $filter . ")";
    }
    if ($one_table['rule_filter']) {
        $filter .= " AND (" . filter_tpl_replace($one_table['rule_filter']) . ")";
    }

    // Вывод количества строк для подтаблицы
    $result = data_select_field($one_table['id'], 'COUNT(*) AS cnt',
        'status = ' . ($autostatus ? $main_line['status'] : ($main_line['status'] < 3 ? 0 : 3)) . ' AND ' . $link_search . $filter);
    $row = sql_fetch_assoc($result);
    return $row['cnt'];
}

function check_finish_update()
{
    global $config;
    $file_path_new = $config['site_path'] . '/revision';
    $file_path_old = $config['site_path'] . '/runtime/revision.old';
    if (file_exists($file_path_new)) {
        if (file_exists($file_path_old)) {
            if (md5_file($file_path_new) !== md5_file($file_path_old) && copy($file_path_new, $file_path_old)) {
                header('Location:login.php?logout');
            }
        } else {
            copy($file_path_new, $file_path_old);
        }
    }
}

function check_valid_image($name_image, $path_image)
{
    if (!$name_image || !$path_image) {
        return false;
    }
    $white_list = [
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'gif' => 'image/gif',
        'png' => 'image/png',
        'bmp' => 'image/bmp',
        'ico' => 'image/x-icon',
        'svg' => 'image/svg+xml',
    ];
    $check_flag = false;
    foreach ($white_list as $good_ext => $good_mime) {
        if (preg_match('/' . $good_ext . '$/i', $name_image)) {
            $check_flag = true;
            break;
        }
    }
    if ($check_flag && $good_ext != 'svg') {
        $imageinfo = getimagesize($path_image);
        if (!$imageinfo || !in_array($imageinfo['mime'], $white_list, true)) {
            $check_flag = false;
        }
    }

    return $check_flag;
}

/**
 * Формирует имя домена для строки копирайта
 * @throws Exception
 * @return array
 * [
 *    'title' => <str>, // Наименование копирайта
 *    'host' => <str>, // Домен копирайта
 * ]
 */
function get_copyright_name()
{
    global $config;

    // домены по умолчанию из ключа
    $def_names = [
        1 => 'clientbase.ru',
        2 => 'basemaster.com',
    ];

    // партнерские домены
    $found = 0;
    $domains = get_partners_domains();
    foreach ($domains['info'] as $host => $info) {
        if ($host && (strpos($_SERVER['HTTP_HOST'], $host) !== false)) {
            $found = 1;
            break;
        }
    }
    if (!$found) {
        $host = $def_names[$config['copyright']] ?? 'clientbase.ru';
        $info = ['whitelabel' => 0, 'copyright' => 1];
    }

    return [
        'host' => $host,
        'title' => ucfirst($host),
        'whitelabel' => $info['whitelabel'],
        'copyright' => $info['copyright'],
        'application' => $info['application'],
    ];
}

/**
 * Возвращает список партнерских доменов
 * Кеширует значение на PARTNER_DOMAINS_TTL секунд
 *
 * @throws Exception
 * @return array
 * [
 *  'hosts' => [<str>, <str>, ...], /// список доменов партнеров
 *  'time' => <int>, /// метка времени Unix об истечении срока кеширования
 *  'human_time' => <str>  /// метка времени time в формате ATOM
 * ]
 */
function get_partners_domains()
{
    $domains = [];
    $now = time();
    if (file_exists(PARTNER_DOMAINS_CACHE)) {
        $domains = json_decode(base64_decode(file_get_contents(PARTNER_DOMAINS_CACHE)), true);
    }
    if (!is_array($domains) || !isset($domains['time']) || $domains['time'] < $now) {
        $cache = fopen(PARTNER_DOMAINS_CACHE, 'a');
        if ($cache !== false) { // смогли открыть файл на запись, иначе ничего не делаем
            $domains['time'] = $now + PARTNER_DOMAINS_TTL;
            $domains['human_time'] = date(DATE_ATOM, $domains['time']);
            $hosts = send_command('any', PARTNER_DOMAINS_URL . '?v=3', PARTNER_DOMAINS_TIMEOUT);
            if (is_array($hosts)) {
                $domains['hosts'] = array_keys($hosts);
                $domains['info'] = $hosts;
            }
            if (flock($cache, LOCK_EX|LOCK_NB)) {
                ftruncate($cache, 0);
                fwrite($cache, base64_encode(json_encode($domains)));
                fflush($cache);
                flock($cache, LOCK_UN);
            }
            fclose($cache);
        }
    }
    if (!isset($domains['hosts']) || !is_array($domains['hosts'])) {
        $domains['hosts'] = [];
        $domains['info'] = [];
    }
    return $domains;
}

/**
 * Функция совместимости вызова count() с PHP 8.1
 * Проверяет, является ли аргумент массивом, если нет - возвращает 0
 * 
 * @param array $countable
 * @return int
 */
function cb_count($countable)
{
    if (is_array($countable) || ($countable instanceof Countable)) {
        return count($countable);
    } elseif ($countable) {
        return 1;
    } else {
        return 0;
    }
}

/**
 * Функция для удобства автозамены count на count_cb, чтобы без ошибок прогонять несколько раз
 */
function count_cb($countable)
{
    return cb_count($countable);
}

/**
 * Функция совместимости вызова implode с PHP 8.1
 * Проверяет, является ли второй аргумент массивом, если нет - ничего не возвращает
 */
function implode_cb($delimiter, $array)
{
    if (is_array($array)) {
        return implode($delimiter, $array);
    } else {
        return "";
    }
}

/**
 * Функция совместимости вызова explode с PHP 8.1
 * Проверяет, является ли второй аргумент строкой, если нет - возвращаем пустой массив
 */
function explode_cb($delimiter, $string)
{
    if (is_string($string)) {
        return explode($delimiter, $string);
    } else {
        return [];
    }
}

/**
 * Функция совместимости вызова in_array с PHP 8.1
 * Проверяет, является ли второй аргумент массивом, если нет - возвращает false
 */
function in_array_cb($needle, $array)
{
    if (!empty($array) && is_array($array)) {
        return in_array($needle, $array);
    } else {
        return false;
    }
}

/**
 * Функция проверки и получения значения элемента массива
 * Проверяет, существует ли индекс массива и возвращет значение либо null
 * Пример использования: вместо "$var = $array[$param1][$param2]" пишем "$var = cb_isset($array, $param1, $param2)"
 * 
 * @param array $array
 * @return mixed
 */
function cb_isset(&$array)
{
    $var = $array;

    $numArgs = func_num_args();
    if ($numArgs > 1) {
        $args = func_get_args();
        for ($i = 1; $i < $numArgs; $i++) {
            $index = $args[$i];
            if (isset($var[$index])) {
                $var = $var[$index];
            } else {
                $var = null;
                break;
            }
        }
    }

    return $var;
}

/*
//Для совместимости с PHP8 возможно понадобится
if (!function_exists("get_magic_quotes_gpc")) {
    function get_magic_quotes_gpc() {
        return false;
    }
}
if (!function_exists("get_magic_quotes_runtime")) {
    function get_magic_quotes_runtime() {
        return false;
    }
}
*/