Файл admin.extensions.php в Cotonti CMF

Файл "admin.extensions.php" в движке Cotonti CMF является важным компонентом административной части системы, предназначенным для работы с расширениями — модулями и плагинами. Он включает в себя обработку запросов, отображение информации об установленных расширениях, управление их состоянием, а также взаимодействие с базой данных и другими частями системы.

Основное назначение файла

Файл предназначен для управления расширениями в админ-панели сайта. Он выполняет несколько ключевых задач:

  1. Отображение списка установленных расширений: Файл выполняет запросы к базе данных, извлекает информацию о модулях и плагинах, отображает их пользователю с возможностью фильтрации и сортировки.
  2. Проверка состояния расширений: Файл проверяет, активны ли расширения, отображает их версии, количество установленных частей и настройки.
  3. Управление зависимостями: Он проверяет и отображает зависимости между расширениями (например, если плагин зависит от другого модуля или плагина).
  4. Взаимодействие с правами доступа: Для каждого расширения выводится информация о правах доступа, ограничениях для пользователей и других настройках безопасности.
  5. Хуки и плагины: Файл обрабатывает хуки, позволяя другим расширениям вставлять свой код в определенные места данного кода.

Основные задачи и условия, которые выполняет код

  • Получение данных о расширениях: Код выполняет SQL-запросы для получения списка установленных расширений, их категорий, состояний и метаданных (версии, авторы, права доступа и т. д.).
  • Фильтрация по статусу: Есть возможность фильтровать расширения по статусу (установлены или нет), а также сортировать их по категориям или алфавиту.
  • Обработка ошибок: Если для расширения указана ошибка, она отображается пользователю в административной панели.
  • Отображение и вывод информации: Файл использует шаблонный движок для вывода информации на странице (с помощью $t->assign и $t->parse).
  • Интеграция с системой прав доступа: Интеграция с функцией cot_auth_getmask для определения прав доступа для различных пользователей.
  • Подключение хуков: В коде имеются места для добавления внешнего кода через хуки, что позволяет расширить функциональность без изменения основного кода.

Список методов и функций

В данном фрагменте кода явно не определены собственные функции или методы, однако используются следующие системные функции и методы:

  • cot_import() — для получения данных из входных параметров.
  • cot_url() — для генерации URL на страницы административной панели.
  • cot_extension_list_info() — для получения списка расширений.
  • cot_getextplugins() — для получения списка подключенных плагинов (расширений).
  • cot_langfile() — для загрузки языковых файлов.
  • cot_get_extensionparams() — для получения параметров расширений.
  • cot_plugin_active() — для проверки активности плагинов.
  • cot_infoget() — для получения информации о расширениях.

Характеристика стиля PHP

  • Использование шаблонного движка: В коде активно используется шаблонный движок для отображения данных. Через объект $t выполняются операции присваивания значений в шаблон и его парсинг ($t->assign, $t->parse).
  • Открытые и явные переменные: Применяется довольно явное объявление переменных и их использование. Код не сильно скрывает детали реализации, что делает его более прозрачным.
  • Процедурный стиль программирования: В коде наблюдается доминирование процедурного подхода. В частности, используется множество прямых SQL-запросов, а также конструкций if и foreach для обработки данных.
  • Использование современных PHP-функций: Код использует современные функции PHP, такие как isset(), empty(), операторы типа ??, массивы и работу с объектами.
  • Отсутствие ООП: В коде не наблюдается использования объектно-ориентированного подхода в чистом виде. Хотя используются объекты для работы с шаблонами и базой данных, большая часть логики выполняется в процедурной манере.

Резюме

Файл "admin.extensions.php" является важным элементом административной панели Cotonti, предназначенным для управления расширениями (модулями и плагинами). Он извлекает, отображает и управляет данными о расширениях, их зависимостями и состоянием, а также взаимодействует с правами доступа и системой безопасности. Код написан в процедурном стиле с активным использованием шаблонного движка для вывода данных и взаимодействия с пользователем.

находится по адресу: /system/admin/admin.extensions.php движка php-фреймворка Cotonti.

 

<?php
/**
 * Extension administration
 *
 * @package Cotonti
 * @copyright (c) Cotonti Team
 * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
 */

// Задается пространство имен и подключение необходимых классов для работы с расширениями
use cot\extensions\ExtensionsControlService; // Используется класс для управления расширениями
use cot\extensions\ExtensionsDictionary;     // Используется класс для работы с базой данных расширений
use cot\extensions\ExtensionsService;        // Используется класс для основной логики работы с расширениями

(defined('COT_CODE') && defined('COT_ADMIN')) or die('Wrong URL.'); // Проверка, что файл был вызван в правильном контексте, иначе ошибка

/**
 * @var array $cot_plugins
 */

// Извлекаются права доступа текущего пользователя
list(Cot::$usr['auth_read'], Cot::$usr['auth_write'], Cot::$usr['isadmin']) = cot_auth('admin', 'a'); // Получение прав пользователя для раздела администрирования
cot_block(Cot::$usr['isadmin']); // Проверка, что у пользователя есть права администратора

require_once cot_incfile('auth'); // Подключение файла аутентификации для дополнительной проверки прав

$t = new XTemplate(cot_tplfile('admin.extensions', 'core')); // Создание экземпляра шаблонизатора для администрирования расширений

$adminPath[] = array (cot_url('admin', 'm=extensions'), Cot::$L['Extensions']); // Добавление ссылки на раздел расширений в путь навигации
$adminTitle = Cot::$L['Extensions']; // Установка заголовка страницы как "Расширения"

$pl = cot_import('pl', 'G', 'ALP'); // Получение параметра 'pl' из глобального массива GET (может быть названием плагина)
$mod = cot_import('mod', 'G', 'ALP'); // Получение параметра 'mod' из глобального массива GET (может быть названием модуля)
$part = cot_import('part', 'G', 'TXT'); // Получение параметра 'part' из глобального массива GET (часть или категория плагина)
$sort = cot_import('sort', 'G', 'ALP'); // Получение параметра 'sort' из глобального массива GET (параметр сортировки)

Этот участок кода отвечает за инициализацию страницы администрирования расширений, а также проверку прав доступа пользователя и подготовку данных для дальнейшей работы с расширениями в системе.

 

if (empty($mod)) { // Если переменная $mod пустая (нет модуля)
    if (empty($pl)) { // Если переменная $pl пустая (нет плагина)
        if (!empty($a) && $a != 'hooks') { // Если параметр $a существует и не равен 'hooks'
            cot_die(); // Завершаем выполнение с ошибкой
        }

    } else { // Если передан плагин
        $is_module = false; // Устанавливаем флаг, что это не модуль
        $code = $pl; // Присваиваем название плагина переменной $code
        $arg = 'pl'; // Устанавливаем тип как 'pl' (плагин)
        $dir = Cot::$cfg['plugins_dir']; // Устанавливаем директорию плагинов
        $type = ExtensionsDictionary::TYPE_PLUGIN; // Устанавливаем тип как плагин
    }

} else { // Если передан модуль
    $is_module = true; // Устанавливаем флаг, что это модуль
    $code = $mod; // Присваиваем название модуля переменной $code
    $arg = 'mod'; // Устанавливаем тип как 'mod' (модуль)
    $dir = Cot::$cfg['modules_dir']; // Устанавливаем директорию модулей
    $type = ExtensionsDictionary::TYPE_MODULE; // Устанавливаем тип как модуль
}

$status[0] = $R['admin_code_paused']; // Статус 'приостановлен'
$status[1] = $R['admin_code_running']; // Статус 'работает'
$status[2] = $R['admin_code_partrunning']; // Статус 'частично работает'
$status[3] = $R['admin_code_notinstalled']; // Статус 'не установлен'
$status[4] = $R['admin_code_missing']; // Статус 'отсутствует'
$found_txt[0] = $R['admin_code_missing']; // Текст для статуса 'отсутствует'
$found_txt[1] = $R['admin_code_present']; // Текст для статуса 'присутствует'
unset($disp_errors); // Удаляем переменную $disp_errors

$extensionsService = ExtensionsService::getInstance(); // Получаем экземпляр сервиса расширений

/* === Hook === */
foreach (cot_getextplugins('admin.extensions.first') as $pl) { // Включаем плагины, связанные с хуком 'admin.extensions.first'
    include $pl; // Подключаем каждый плагин
}
/* ===== */

Этот участок кода отвечает за определение того, работает ли с плагином или модулем, а также за настройку статусов и текстов, которые используются для отображения состояния расширений в админке. В нем также настраивается использование хуков для подключения внешних плагинов.

 

switch($a) { // В зависимости от значения переменной $a, выполняем соответствующие действия
    /* =============== */
    case 'details': // Если значение $a равно 'details', выполняем следующий блок
    /* =============== */
        $extDir = $dir . '/' . $code; // Устанавливаем путь к директории расширения, комбинируя директорию и код расширения
        if (!file_exists($extDir)) { // Если директория расширения не существует
            cot_die_message(404); // Завершаем выполнение с ошибкой 404 (не найдено)
        }
        $ext_info = $extDir . '/' . $code . '.setup.php'; // Устанавливаем путь к файлу настроек расширения
        $info = false; // Инициализируем переменную для информации о расширении как false
        $exists = file_exists($ext_info); // Проверяем, существует ли файл настроек расширения
        if ($exists) { // Если файл настроек существует
            $old_ext_format = false; // Устанавливаем флаг старого формата в false
            $info = cot_infoget($ext_info, 'COT_EXT'); // Загружаем информацию о расширении
            if (!$info && cot_plugin_active('genoa')) { // Если информация не загружена и плагин 'genoa' активен
                // Try to load old format info
                $info = cot_infoget($ext_info, 'SED_EXTPLUGIN'); // Пытаемся загрузить информацию в старом формате
                $old_ext_format = true; // Устанавливаем флаг старого формата в true
                cot_message('ext_old_format', 'warning'); // Отправляем сообщение с предупреждением о старом формате
            }
        }

        if ($info == false) { // Если информация о расширении не загружена
            // Failed to load info block
            // Lets use default data
            $info = array( // Инициализируем массив с дефолтными данными для расширения
                'Code' => $code,
                'Name' => $code,
                'Description' => '',
                'Category' => '',
                'Version' => '',
                'Date' => '',
                'Author' => '',
                'Copyright' => '',
                'Notes' => '',
                'Auth_members' => '',
                'Lock_members' => '',
                'Auth_guests' => '',
                'Lock_guests' => '',
                'Requires_modules' => '',
                'Requires_plugins' => '',
                'Recommends_modules' => '',
                'Recommends_plugins' => ''
            );
        }

        $extensionControlService = ExtensionsControlService::getInstance(); // Получаем экземпляр сервиса управления расширениями

        switch($b) { // В зависимости от значения переменной $b выполняем следующие действия
            case 'install': // Если значение $b равно 'install', выполняем установку расширения
                $installed_modules = $db->query("SELECT ct_code FROM $db_core WHERE ct_plug = 0")->fetchAll(PDO::FETCH_COLUMN); // Получаем все установленные модули
                $installed_plugins = $db->query("SELECT ct_code FROM $db_core WHERE ct_plug = 1")->fetchAll(PDO::FETCH_COLUMN); // Получаем все установленные плагины
                $dependencies_satisfied = cot_extension_dependencies_statisfied($code, $is_module, $installed_modules, $installed_plugins); // Проверяем удовлетворение зависимостей для расширения
                if ($dependencies_satisfied) { // Если все зависимости удовлетворены
                    $result = cot_extension_install($code, $is_module); // Устанавливаем расширение
                }
            break;

            case 'update': // Если значение $b равно 'update', выполняем обновление расширения
                $result = cot_extension_install($code, $is_module, true, true); // Обновляем расширение
                break;

Этот участок кода отвечает за обработку деталей расширений в админке. Он выполняет проверку существования расширения, загружает его информацию, а затем в зависимости от команды ($b) выполняет установку или обновление расширения, учитывая зависимости.

 

$info = array( // Инициализируем массив с дефолтными данными для расширения
    'Code' => $code, // Код расширения, который равен переменной $code
    'Name' => $code, // Имя расширения, которое также равно переменной $code
    'Description' => '', // Пустое описание расширения
    'Category' => '', // Пустая категория расширения
    'Version' => '', // Пустая версия расширения
    'Date' => '', // Пустая дата расширения
    'Author' => '', // Пустое имя автора расширения
    'Copyright' => '', // Пустое поле для информации о копирайте
    'Notes' => '', // Пустые заметки для расширения
    'Auth_members' => '', // Пустой доступ для зарегистрированных пользователей
    'Lock_members' => '', // Пустая блокировка для зарегистрированных пользователей
    'Auth_guests' => '', // Пустой доступ для гостей
    'Lock_guests' => '', // Пустая блокировка для гостей
    'Requires_modules' => '', // Пустые требования для модулей
    'Requires_plugins' => '', // Пустые требования для плагинов
    'Recommends_modules' => '', // Пустые рекомендации для модулей
    'Recommends_plugins' => '' // Пустые рекомендации для плагинов
);

 

 

case 'uninstall': // Обрабатываем случай, когда требуется удалить расширение
    /* === Hook  === */
    foreach (cot_getextplugins('admin.extensions.uninstall.first') as $pl) { // Запускаем плагины, связанные с процессом деинсталляции
        include $pl; // Подключаем плагин
    }
    /* ===== */

    if (cot_check_xg(false)) { // Проверяем, если запрос прошел проверку X-Token
        // Check if there are extensions installed depending on this one
        $dependencies_satisfied = true; // Переменная для проверки зависимостей
        $res = $db->query("SELECT ct_code, ct_plug FROM $db_core ORDER BY ct_plug, ct_code"); // Получаем все расширения из базы данных
        foreach ($res->fetchAll() as $row) { // Перебираем все расширения
            $ext = $row['ct_code']; // Код текущего расширения
            $dir_ext = $row['ct_plug'] ? Cot::$cfg['plugins_dir'] : Cot::$cfg['modules_dir']; // Определяем путь к плагину или модулю
            $dep_ext_info = $dir_ext . '/' . $ext . '/' . $ext . '.setup.php'; // Путь к файлу setup.php расширения
            if (file_exists($dep_ext_info)) { // Проверяем, существует ли файл setup.php для зависимого расширения
                $dep_info = cot_infoget($dep_ext_info, 'COT_EXT'); // Получаем информацию о расширении
                if (!$dep_info && cot_plugin_active('genoa')) { // Если информация не найдена и плагин 'genoa' активен
                    // Try to load old format info
                    $dep_info = cot_infoget($dep_ext_info, 'SED_EXTPLUGIN'); // Пробуем загрузить информацию в старом формате
                }
                $dep_field = $is_module ? 'Requires_modules' : 'Requires_plugins'; // Определяем поле для зависимостей
                if (in_array($code, explode(',', $dep_info[$dep_field]))) { // Если текущее расширение указано как зависимость
                    cot_error(cot_rc('ext_dependency_uninstall_error', array( // Выводим ошибку о зависимости
                        'type' => $row['ct_plug'] ? Cot::$L['Plugin'] : Cot::$L['Module'], // Указываем тип (плагин или модуль)
                        'name' => $dep_info['Name'] // Указываем имя зависимого расширения
                    )));
                    $dependencies_satisfied = false; // Устанавливаем флаг зависимости как неудовлетворенную
                }
            }
        }

        if ($dependencies_satisfied) { // Если все зависимости удовлетворены
            $result = cot_extension_uninstall($code, $is_module); // Выполняем деинсталляцию расширения
        }
        $adminPath[] = Cot::$L['adm_opt_uninstall']; // Добавляем ссылку на деинсталляцию в путь навигации

    } else { // Если проверка X-Token не пройдена
        $url = cot_url('admin', "m=extensions&a=details&$arg=$code&b=uninstall&x={$sys['xk']}"); // Формируем URL для подтверждения деинсталляции
        cot_message(cot_rc('ext_uninstall_confirm', array('url' => $url)), 'error'); // Показываем сообщение об ошибке с ссылкой на подтверждение
        cot_redirect(cot_url('admin', "m=extensions&a=details&$arg=$code", '', true)); // Перенаправляем на страницу с расширением
    }
    break;

Объяснение:

  • Этот блок кода обрабатывает запрос на удаление расширения (модуль или плагин).
  • Если проверка X-Token проходит, происходит проверка зависимостей расширений: если текущее расширение зависит от других, которые ещё не удалены, выводится ошибка.
  • Если зависимости удовлетворены, расширение деинсталлируется, иначе отображается ошибка.
  • Если проверка X-Token не проходит, система предлагает пользователю подтвердить удаление расширения по предоставленной ссылке.
case 'pause': // Обрабатываем запрос на паузу расширения
    $extensionControlService->pause($code); // Приостанавливаем расширение по коду
    cot_message('adm_paused'); // Показываем сообщение о приостановке
    break;

case 'unpause': // Обрабатываем запрос на возобновление работы расширения
    $extensionControlService->resume($code); // Возобновляем работу расширения по коду
    cot_message('adm_running'); // Показываем сообщение о возобновлении работы
    break;

case 'pausepart': // Обрабатываем запрос на приостановку части расширения
    $extensionControlService->pause($code, $part); // Приостанавливаем часть расширения по коду и части
    cot_message('adm_partstopped'); // Показываем сообщение о приостановке части
    break;

case 'unpausepart': // Обрабатываем запрос на возобновление работы части расширения
    $extensionControlService->resume($code, $part); // Возобновляем работу части расширения по коду и части
    cot_message('adm_partrunning'); // Показываем сообщение о возобновлении работы части
    break;
 } //   закрываем switch($b)

Объяснение:

  • В этом участке кода обрабатываются запросы на изменение состояния расширений: приостановка и возобновление работы.
  • Каждый из случаев (pause, unpause, pausepart, unpausepart) вызывает соответствующий метод pause() или resume() в сервисе управления расширениями и выводит сообщение о результате действия.

 

if (!empty($b)) { // Если переменная $b не пуста
    Cot::$db->update(Cot::$db->users, array('user_auth' => ''), "user_auth != ''"); // Обновляем записи в таблице пользователей, сбрасывая авторизацию
    if (Cot::$cache) { // Если существует кэш
        Cot::$cache->clear(); // Очищаем кэш
    }
    cot_redirect(cot_url('admin', "m=extensions&a=details&$arg=$code", '', true)); // Перенаправляем на страницу деталей расширения
}

$registeredParts = []; // Инициализируем пустой массив для зарегистрированных частей расширения
$parts = []; // Инициализируем пустой массив для частей расширения
if ($exists) { // Если расширение существует
    // Собираем все части из директории расширения
    $handle = opendir($dir . '/' . $code); // Открываем директорию расширения
    while ($f = readdir($handle)) { // Читаем файлы в директории
        if (
            preg_match("#^$code(\.([\w\.]+))?.php$#", $f, $mt) // Проверяем, соответствует ли имя файла шаблону
            && (!isset($mt[2]) || !in_array($mt[2], $cot_ext_ignore_parts)) // И проверяем, что часть не находится в списке игнорируемых
        ) {
            $parts[] = $f; // Добавляем файл в массив частей
        }
    }
    closedir($handle); // Закрываем директорию

    // ...И собираем части из базы данных
    // Может быть несколько записей с одинаковым pl_part. Одна для каждого хука (для частей с несколькими хуками).
    // Но все они содержат информацию о одном и том же расширении. Таблица не нормализована.
    $registeredParts = []; // Инициализируем массив для зарегистрированных частей
    $query = Cot::$db->query(
        'SELECT * FROM ' . Cot::$db->plugins . ' WHERE pl_code = :code', // Запрашиваем все части для данного расширения
        ['code' => $code] // Параметр для запроса
    );
    while ($row = $query->fetch()) { // Для каждой записи
        if (!isset($registeredParts[$row['pl_part']]['hooks'])) { // Если еще не существует ключа для хуков в данной части
            $registeredParts[$row['pl_part']]['hooks'] = []; // Инициализируем массив для хуков
        }
        $registeredParts[$row['pl_part']]['hooks'][$row['pl_hook']] = [ // Добавляем хук в массив
            'hook' => $row['pl_hook'],
            'order' => (int) $row['pl_order'], // Сохраняем порядок хуков
        ];
        unset($row['pl_hook'], $row['pl_order']); // Убираем ненужные данные
        $registeredParts[$row['pl_part']] = array_merge($registeredParts[$row['pl_part']], $row); // Мержим данные с остальными частями
    }
    $query->closeCursor(); // Закрываем курсор запроса

    // Дополняем список частей расширения, если оно не зарегистрировано
    foreach ($registeredParts as $reg_data) {
        if ($reg_data['pl_code'] == $code) { // Если код расширения совпадает
            $f = str_replace($code . '/', '', $reg_data['pl_file']); // Извлекаем имя файла без пути
            if (!in_array($f, $parts)) { // Если части ещё нет в массиве
                $parts[] = $f; // Добавляем часть в массив
            }
        }
    }

    // Получаем значения прав доступа для членов и гостей
    $info['Auth_members'] = cot_auth_getvalue($info['Auth_members']);
    $info['Lock_members'] = cot_auth_getvalue($info['Lock_members']);
    $info['Auth_guests'] = cot_auth_getvalue($info['Auth_guests']);
    $info['Lock_guests'] = cot_auth_getvalue($info['Lock_guests']);

} else { // Если расширение не существует в директории
    $row = Cot::$db->query('SELECT * FROM ' . Cot::$db->core . ' WHERE ct_code = '.
        Cot::$db->quote($code))->fetch(); // Запрашиваем данные о расширении из основной таблицы
    if ($row) { // Если запись найдена
        $info['Name'] = $row['ct_title']; // Присваиваем название расширения
        $info['Version'] = $row['ct_version']; // Присваиваем версию расширения
    }
}

Объяснение:

  • В этом участке кода происходит проверка состояния расширения, сбор информации о его частях как из файловой системы, так и из базы данных.
  • В случае наличия переменной $b, очищается кэш и происходит перенаправление на страницу расширения.
  • Собираются части расширения как из файловой системы, так и из базы данных, а также проверяются зависимости между ними.
  • Данные о расширении, такие как права доступа для участников и гостей, обновляются через функции cot_auth_getvalue.
  • Если расширение не найдено в директории, производится запрос к основной таблице для получения информации.

 

$ext_info = cot_get_extensionparams($code, $is_module); // Получаем параметры расширения с указанным кодом и типом (модуль или плагин)
$adminPath[] = [cot_url('admin', ['m' => 'extensions', 'a' => 'details', $arg => $code]), $ext_info['name']]; // Добавляем ссылку на страницу деталей расширения в путь навигации, используя имя расширения

$isInstalled = $extensionsService->isInstalled($code, $type, true); // Проверяем, установлено ли расширение с указанным кодом и типом (модуль или плагин)

$sql = Cot::$db->query(
    'SELECT COUNT(*) FROM ' . Cot::$db->config . ' WHERE config_owner = :type AND config_cat = :code ' // Запрашиваем количество конфигураций для данного типа расширения (модуль/плагин) и его кода
    . 'AND config_type NOT IN (' . COT_CONFIG_TYPE_HIDDEN . ', ' . COT_CONFIG_TYPE_SEPARATOR . ') ' // Исключаем конфигурации типа скрытого и разделителя
    . "AND (config_subcat IN ('', '__default') OR config_subcat IS NULL)", // Учитываем только конфигурации с пустым или дефолтным подкатегориями
    ['type' => $type, 'code' => $code] // Параметры для запроса (тип и код расширения)
);
$totalConfig = $sql->fetchColumn(); // Получаем количество таких конфигураций

Объяснение:

  • Этот участок кода выполняет несколько ключевых действий:
    1. Получает параметры расширения с помощью cot_get_extensionparams и добавляет ссылку на страницу подробностей этого расширения в навигационный путь.
    2. Проверяет, установлено ли расширение через сервис ExtensionsService.
    3. Выполняет SQL-запрос к базе данных для подсчета количества конфигураций, связанных с данным расширением, с исключением определенных типов конфигураций (скрытые и разделители).

 

if (count($parts) > 0) { // Если количество элементов в массиве $parts больше 0 (т.е. есть части для обработки)
    sort($parts); // Сортируем массив $parts в порядке возрастания

    /* === Hook - Part1 : Set === */
    $extp = cot_getextplugins('admin.extensions.details.part.loop'); // Получаем плагины, которые подключаются к хуку 'admin.extensions.details.part.loop'
    /* ===== */

    foreach ($parts as $i => $x) { // Для каждой части в массиве $parts
        $extplugin_file = $dir . '/' . $code . '/' . $x; // Формируем полный путь к файлу плагина, добавляя код расширения и часть
        $extensionPart = []; // Инициализируем пустой массив для данных части расширения
        if (file_exists($extplugin_file)) { // Если файл существует
            $extensionPart = cot_infoget($extplugin_file, 'COT_EXT'); // Получаем информацию о расширении из файла с помощью функции cot_infoget
            $hooks = explode(',', $extensionPart['Hooks']); // Разделяем строку хуков на массив по запятой
            $extensionPart['Hooks'] = []; // Инициализируем пустой массив для хуков
            $hookOrders = (isset($extensionPart['Order']) && $extensionPart['Order'] !== '') // Если указаны порядки хуков
                ? explode(',', $extensionPart['Order']) // Разделяем строку порядков хуков по запятой
                : []; // Если порядок не указан, создаем пустой массив
            $hookOrders = array_map(function($el) { return (int) trim($el); }, $hookOrders); // Преобразуем элементы массива $hookOrders в целые числа и удаляем лишние пробелы
            foreach ($hooks as $key => $hook) { // Для каждого хука
                $hook = trim($hook); // Убираем лишние пробелы вокруг хука
                if (empty($hook)) { // Если хук пустой
                    continue; // Пропускаем текущую итерацию
                }
                $extensionPart['Hooks'][$hook] = [ // Добавляем хук в массив хуков
                    'hook' => $hook, // Указываем сам хук
                    'order' => isset($hookOrders[$key]) ? (int) $hookOrders[$key] : COT_PLUGIN_DEFAULT_ORDER, // Указываем порядок хука (по умолчанию COT_PLUGIN_DEFAULT_ORDER)
                ];
            }
            unset($hooks, $hookOrders); // Удаляем временные переменные $hooks и $hookOrders
        }

 

if (empty($extensionPart['Hooks'])) { // Если массив хуков пустой
    $extensionPart['Hooks'] = []; // Инициализируем его как пустой массив
}

$info_part = preg_match("#^$code\.([\w\.]+).php$#", $x, $mt) ? $mt[1] : 'main'; // Проверяем, соответствует ли имя файла расширения шаблону и извлекаем часть, иначе присваиваем 'main'

$extensionPart['Status'] = 3; // Устанавливаем статус части расширения по умолчанию в 3 (не установлена)
$extensionPart['installedOrder'] = null; // Устанавливаем порядок установки части как null
if (isset($registeredParts[$info_part])) { // Если эта часть уже зарегистрирована
    $extensionPart['Status'] = $registeredParts[$info_part]['pl_active']; // Присваиваем статус части на основе данных о зарегистрированной части
}

// проверка на незарегистрированные хуки
$not_registred = []; // Инициализируем пустой массив для незарегистрированных хуков
if ($extensionPart['Status'] == 1) { // Если часть активна (статус 1)
    foreach ($extensionPart['Hooks'] as $hook) { // Для каждого хука в массиве хуков
        $regsistred_by_hook = isset($cot_plugins[$hook['hook']]) ? $cot_plugins[$hook['hook']] : null; // Получаем плагины, зарегистрированные для этого хука
        if (is_array($regsistred_by_hook) && sizeof($regsistred_by_hook)) { // Если для хука есть зарегистрированные плагины
            $found = false; // Инициализируем флаг, который будет указывать на нахождение плагина
            foreach ($regsistred_by_hook as $reg_data) { // Для каждого зарегистрированного плагина
                if ($reg_data['pl_file'] == $code . '/' . $x) { // Если путь к файлу плагина совпадает с текущим расширением
                    $found = true; // Устанавливаем флаг в true
                    break; // Выходим из цикла
                }
            }
            if (!$found) { // Если плагин не найден среди зарегистрированных
                $not_registred[] = $hook['hook']; // Добавляем хук в массив незарегистрированных
            }
        } else {
            $not_registred[] = $hook['hook']; // Если для хука нет зарегистрированных плагинов, добавляем его в массив незарегистрированных
        }
    }
}

 

$deleted = []; // Инициализируем пустой массив для удалённых хуков

// проверка на удалённые хуки
if (file_exists($extplugin_file)) { // Если файл плагина существует
    foreach ($registeredParts as $reg_data) { // Для каждого зарегистрированного элемента
        if ($reg_data['pl_file'] !== $code . '/' . $x) { // Если файл плагина не соответствует текущему
            continue; // Пропускаем эту итерацию цикла
        }
        foreach ($reg_data['hooks'] as $registeredHook) { // Для каждого зарегистрированного хука
            if (!isset($extensionPart['Hooks'][$registeredHook['hook']])) { // Если хук не найден в текущем расширении
                $deleted[] = $registeredHook['hook']; // Добавляем хук в массив удалённых
            }
        }
    }
}

 

if ($isInstalled && (!file_exists($extplugin_file)) || sizeof($deleted) > 0 || sizeof($not_registred) > 0) { // Если расширение установлено, но файл плагина отсутствует, или есть удалённые/незарегистрированные хуки
    $extensionPart['Error'] = Cot::$L['adm_hook_changed']; // Устанавливаем сообщение об ошибке, что хуки были изменены

    if (sizeof($not_registred)) { // Если есть незарегистрированные хуки
        $extensionPart['Error'] .= cot_rc('adm_hook_notregistered', array('hooks' => implode(', ', $not_registred))); // Добавляем сообщение об незарегистрированных хуках
    }

    if (sizeof($deleted)) { // Если есть удалённые хуки
        $extensionPart['Error'] .= cot_rc('adm_hook_notfound', ['hooks' => implode(', ', $deleted)]); // Добавляем сообщение об удалённых хуках
    }

    if (!file_exists($extplugin_file)) { // Если файл плагина не найден
        $extensionPart['Error'] .= cot_rc('adm_hook_filenotfound', array('file' => $extplugin_file)); // Добавляем сообщение о том, что файл плагина не найден
    }

    $extensionPart['Error'] .= Cot::$L['adm_hook_updatenote']; // Добавляем уведомление о необходимости обновления
}

$hooks = ''; // Инициализируем переменную для хранения хуков
if (!empty($extensionPart['Hooks'])) { // Если хуки существуют
    $hooks = implode('<br />', array_column($extensionPart['Hooks'], 'hook')); // Собираем список хуков, разделённых переносами строк
}

$installedHooks = ''; // Инициализируем переменную для установленных хуков
if (!empty($registeredParts[$info_part])) { // Если для данной части расширения есть зарегистрированные хуки
    $installedHooks = implode('<br />', array_column($registeredParts[$info_part]['hooks'], 'hook')); // Собираем список установленных хуков
}

 

if (!empty($extensionPart['Error'])) { // Если есть ошибки в расширении
    $t->assign([ // Присваиваем значения в шаблон для отображения ошибок
        'ADMIN_EXTENSIONS_DETAILS_ROW_I_1' => $i+1, // Индекс строки, увеличенный на 1 (для отображения в шаблоне)
        'ADMIN_EXTENSIONS_DETAILS_ROW_PART' => $info_part, // Информация о части расширения
        'ADMIN_EXTENSIONS_DETAILS_ROW_HOOKS' => $hooks, // Хуки, указанные в расширении
        'ADMIN_EXTENSIONS_DETAILS_ROW_HOOKS_INSTALLED' => $installedHooks, // Установленные хуки
        'ADMIN_EXTENSIONS_DETAILS_ROW_FILE' => $x, // Имя файла расширения
        'ADMIN_EXTENSIONS_DETAILS_ROW_ERROR' => $extensionPart['Error'], // Сообщение об ошибке
    ]);
    $t->parse('MAIN.DETAILS.ROW_ERROR_PART'); // Парсим шаблон строки с ошибкой

} else { // Если нет ошибок в расширении

В этом участке кода происходит проверка на наличие ошибок в расширении. Если ошибки есть, данные об ошибке, хука, файле и другие параметры передаются в шаблон для отображения. Если ошибок нет, выполнение кода продолжается дальше.

if (empty($extensionPart['Tags'])) { // Если нет тегов в расширении
    $t->assign([ // Присваиваем в шаблон информацию об ошибке для отсутствия тегов
        'ADMIN_EXTENSIONS_DETAILS_ROW_I_1' => $i+1, // Индекс строки, увеличенный на 1
        'ADMIN_EXTENSIONS_DETAILS_ROW_PART' => $info_part, // Информация о части расширения
    ]);
    $t->parse('MAIN.DETAILS.ROW_ERROR_TAGS'); // Парсим шаблон строки с ошибкой для тегов

} else { // Если теги присутствуют
    $taggroups = explode(';', $extensionPart['Tags']); // Разбиваем теги по группам
    foreach ($taggroups as $taggroup) { // Для каждой группы тегов
        $line = explode(':', $taggroup); // Разбиваем группу на имя шаблона и список тегов
        $line[0] = trim($line[0]); // Убираем лишние пробелы в имени шаблона
        $tplbase = explode('.', preg_replace('#\.tpl$#i', '', $line[0])); // Получаем базовое имя шаблона без расширения
        // Определяем тип контейнера шаблона
        if (in_array($tplbase[0], array('admin', 'users'))) {
            $tpltype = 'core'; // Тип шаблона - ядро
        } elseif (file_exists(Cot::$cfg['plugins_dir'] . '/' . $tplbase[0])) {
            $tpltype = 'plug'; // Тип шаблона - плагин
        } else {
            $tpltype = 'module'; // Тип шаблона - модуль
        }
        $tags = explode(',', $line[1]); // Разбиваем теги по запятой
        $tpl_file = cot_tplfile($tplbase, $tpltype); // Получаем путь к файлу шаблона
        $listtags = $tpl_file.' :<br />'; // Начинаем список тегов
        if (Cot::$cfg['xtpl_cache']) { // Если включен кэш шаблонов
            // Очищаем кэш, если он существует
            $cache_file = str_replace(array('./', '/'), '_', $tpl_file); 
            $cache_path = Cot::$cfg['cache_dir'] . '/templates/' .
                pathinfo($cache_file, PATHINFO_FILENAME );
            $cache_files_ext = array('.tpl','.idx','.tags'); // Расширения файлов кэша
            foreach ($cache_files_ext as $ext) { // Для каждого расширения кэшированных файлов
                if (file_exists($cache_path.$ext)) {
                    unlink($cache_path.$ext); // Удаляем кэшированные файлы
                }
            }
        }
        $tpl_check = new XTemplate($tpl_file); // Загружаем шаблон для проверки тегов
        $tpl_tags = $tpl_check->getTags(); // Получаем теги шаблона
        unset($tpl_tags[array_search('PHP', $tpl_tags)]); // Удаляем тег 'PHP' из списка тегов
        foreach($tags as $k => $v) { // Для каждого тега в группе
            if(mb_substr(trim($v), 0, 1) == '{') { // Если тег начинается с '{'
                $tag = str_replace(array('{','}'),'',$v); // Убираем скобки из тега
                $found = in_array($tag, $tpl_tags); // Проверяем, найден ли тег в шаблоне
                $listtags .= $v.' : '; // Добавляем тег в список
                $listtags .= $found_txt[$found].'<br />'; // Добавляем статус нахождения тега
            } else { // Если тег не содержит фигурные скобки
                $listtags .= $v.'<br />'; // Добавляем тег в список
            }
        }

        $t->assign([ // Присваиваем данные в шаблон для отображения тегов
            'ADMIN_EXTENSIONS_DETAILS_ROW_I_1' => $i + 1, // Индекс строки
            'ADMIN_EXTENSIONS_DETAILS_ROW_PART' => $info_part, // Информация о части расширения
            'ADMIN_EXTENSIONS_DETAILS_ROW_FILE' => $line[0].' :<br />', // Имя шаблона
            'ADMIN_EXTENSIONS_DETAILS_ROW_LISTTAGS' => $listtags, // Список тегов
        ]);
        $t->parse('MAIN.DETAILS.ROW_TAGS'); // Парсим шаблон строки с тегами
    }
}

В этом участке кода происходит обработка тегов, указанных в расширении. Если теги присутствуют, они обрабатываются и проверяются на наличие в шаблоне, при этом очищается кэш шаблонов (если это необходимо). Результаты добавляются в шаблон для отображения информации о тегах и их статусе.

Если теги отсутствуют, выводится соответствующее сообщение об ошибке.

 

$order = array_column($extensionPart['Hooks'], 'order'); // Извлекаем массив значений порядка (order) из хуков расширения
$installedOrder = !empty($registeredParts[$info_part]) ? // Проверяем, если зарегистрированная часть расширения существует
    array_column($registeredParts[$info_part]['hooks'], 'order') // Извлекаем порядок хуков для зарегистрированной части
    : ''; // Если нет, устанавливаем пустую строку
if (empty($installedOrder) || count($order) === count($installedOrder)) { // Если порядок установлен, или их количество совпадает
    if (min($order) === max($order)) { // Если минимальный и максимальный порядок одинаковы
        $order = $order[0]; // Устанавливаем порядок равным единственному значению
    }
    if (!empty($installedOrder) && min($installedOrder) === max($installedOrder)) { // Если установлен порядок для зарегистрированных хуков
        $installedOrder = $installedOrder[0]; // Устанавливаем его в единственное значение
    }
}
if (is_array($order)) { // Если порядок является массивом
    $order = implode(', ', $order); // Преобразуем его в строку, разделённую запятой
}
if (is_array($installedOrder)) { // Если установлен порядок для зарегистрированных хуков как массив
    $installedOrder = !empty($installedOrder) ? implode(', ', $installedOrder) : null; // Преобразуем в строку или null, если пусто
}

$t->assign([ // Присваиваем данные в шаблон для отображения информации о расширении
    'ADMIN_EXTENSIONS_DETAILS_ROW_I_1' => $i + 1, // Индекс строки
    'ADMIN_EXTENSIONS_DETAILS_ROW_PART' => $info_part, // Информация о части расширения
    'ADMIN_EXTENSIONS_DETAILS_ROW_FILE' => $x, // Имя файла
    'ADMIN_EXTENSIONS_DETAILS_ROW_HOOKS' => $hooks, // Список хуков
    'ADMIN_EXTENSIONS_DETAILS_ROW_HOOKS_INSTALLED' => $installedHooks, // Список установленных хуков
    'ADMIN_EXTENSIONS_DETAILS_ROW_ORDER' => $order, // Порядок хуков в расширении
    'ADMIN_EXTENSIONS_DETAILS_ROW_ORDER_INSTALLED' => $installedOrder, // Порядок установленных хуков
    'ADMIN_EXTENSIONS_DETAILS_ROW_STATUS' => $status[$extensionPart['Status']], // Статус расширения
    //'ADMIN_EXTENSIONS_DETAILS_ROW_PART_ODDEVEN' => cot_build_oddeven($ii) // Строка для чётности/нечётности (закомментировано)
]);

В этом участке кода происходит обработка порядка хуков (order). Если для расширения и зарегистрированной части порядок совпадает, он приводится к единому значению. Порядок хуков конвертируется в строку, если это массив. После этого данные о расширении, включая порядок хуков, устанавливаются в шаблон для дальнейшего отображения.

 

if ($extensionPart['Status'] == 3) { // Если статус расширения равен 3 (не установлено)
    $t->parse('MAIN.DETAILS.ROW_PART.ROW_PART_NOTINSTALLED'); // Загружаем шаблон для отображения состояния "не установлено"
} elseif ($extensionPart['Status'] == 1) { // Если статус равен 1 (пауза)
    $t->assign('ADMIN_EXTENSIONS_DETAILS_ROW_PAUSEPART_URL', // Присваиваем URL для действия "пауза" для части расширения
        cot_url('admin', "m=extensions&a=details&$arg=$code&b=pausepart&part=".$info_part)); 
    $t->parse('MAIN.DETAILS.ROW_PART.ROW_PART_PAUSE'); // Загружаем шаблон для состояния "пауза"
} elseif($extensionPart['Status'] == 0) { // Если статус равен 0 (не в паузе)
    $t->assign('ADMIN_EXTENSIONS_DETAILS_ROW_UNPAUSEPART_URL', // Присваиваем URL для действия "отпаузить" для части расширения
        cot_url('admin', "m=extensions&a=details&$arg=$code&b=unpausepart&part=".$info_part));
    $t->parse('MAIN.DETAILS.ROW_PART.ROW_PART_UNPAUSE'); // Загружаем шаблон для состояния "не в паузе"
}

/* === Hook - Part2 : Include === */ 
foreach ($extp as $pl) { // Для каждого расширения из плагинов
    include $pl; // Включаем плагин
}
/* ===== */
$t->parse('MAIN.DETAILS.ROW_PART'); // Загружаем шаблон для части расширения

В этом участке кода проверяется статус части расширения. В зависимости от статуса (не установлено, на паузе, не на паузе) генерируется URL для дальнейших действий и соответствующие шаблоны загружаются для отображения. Также подключаются внешние плагины, если таковые есть.


				}
			}
		}
 

Этот блок закрывает несколько структур:

  1. Цикл foreach ($parts as $i => $x) — он заканчивает обход всех элементов массива $parts, то есть завершает обработку всех частей расширений.

  2. Условие if (count($parts) > 0) — если в начале кода был список частей, то этот if проверяет, что в массиве $parts есть элементы, и весь код внутри этого блока выполняется только если массив не пуст. Эта закрывающая скобка завершает его работу.

В результате, данный участок завершает выполнение основной логики работы с частями расширений и закрывает все активные циклы и условия в этом коде.

 

$structurePartUrl = null; // Инициализация переменной для хранения URL структуры, изначально пусто
$extensionsWithStructure = cot_getExtensionsWithStructure(); // Получение списка расширений, связанных с структурой
if (in_array($code, $extensionsWithStructure, true)) { // Проверка, есть ли текущее расширение в списке расширений со структурой
    $structurePartUrl = cot_url('admin', ['m' => 'structure', 'n' => $code]); // Если да, формируется URL для структуры
}

$installed_ver = Cot::$db->query( // Запрос к базе данных для получения версии установленного расширения
    'SELECT ct_version FROM ' . Cot::$db->core . ' WHERE ct_code = :code', // Запрос к таблице для получения версии по коду расширения
    ['code' => $code] // Параметры запроса, код расширения
)->fetchColumn(); // Извлекаем единственное значение — версию

$params = cot_get_extensionparams($code, $type === ExtensionsDictionary::TYPE_MODULE); // Получение параметров для расширения в зависимости от типа

$extensionCategoryTitle = ''; // Инициализация переменной для хранения названия категории расширения
if (!empty($info['Category'])) { // Если категория расширения не пуста
    $extensionCategoryTitle = Cot::$L['ext_cat_' . $info['Category']] ?? $info['Category']; // Присваиваем название категории или использует значение категории, если нет перевода
}

Здесь реализована логика получения информации о расширении, включая формирование URL для структуры, извлечение версии расширения из базы данных и обработку категории расширения.

 

// Universal tags
$t->assign([ 
    'ADMIN_EXTENSIONS_NAME' => htmlspecialchars($params['name']), // Название расширения, экранированное для безопасности
    'ADMIN_EXTENSIONS_TYPE' => $type === ExtensionsDictionary::TYPE_MODULE
        ? Cot::$L['Module'] // Тип расширения: "Module" или "Plugin", в зависимости от типа
        : Cot::$L['Plugin'], 
    'ADMIN_EXTENSIONS_CODE' => $code, // Код расширения
    'ADMIN_EXTENSIONS_ICON' => $params['icon'], // Иконка расширения
    'ADMIN_EXTENSIONS_DESCRIPTION' => $params['desc'], // Описание расширения
    'ADMIN_EXTENSIONS_NOTES' => $params['notes'], // Примечания к расширению
    'ADMIN_EXTENSIONS_CATEGORY' => $info['Category'], // Категория расширения
    'ADMIN_EXTENSIONS_CATEGORY_TITLE' => $extensionCategoryTitle, // Заголовок категории
    'ADMIN_EXTENSIONS_VERSION' => $info['Version'], // Версия расширения
    'ADMIN_EXTENSIONS_VERSION_INSTALLED' => $installed_ver, // Установленная версия расширения
    'ADMIN_EXTENSIONS_VERSION_COMPARE' => version_compare($info['Version'], $installed_ver), // Сравнение текущей версии с установленной
    'ADMIN_EXTENSIONS_DATE' => $info['Date'], // Дата релиза расширения
    'ADMIN_EXTENSIONS_CONFIG_URL' => cot_url(
        'admin',
        ['m' => 'config', 'n' => 'edit', 'o' => $type, 'p' => $code]
    ), // URL для настройки расширения
    'ADMIN_EXTENSIONS_ADMIN_URL' => $extensionsService->getAdminPageUrl($code, $type), // Административный URL для расширения
    'ADMIN_EXTENSIONS_JUMPTO_URL' => $extensionsService->getPublicPageUrl($code, $type), // Публичный URL для расширения
    'ADMIN_EXTENSIONS_STRUCTURE_URL' => $structurePartUrl, // URL для структуры расширения
    'ADMIN_EXTENSIONS_TOTALCONFIG' => $totalConfig, // Общая конфигурация
    'ADMIN_EXTENSIONS_INSTALL_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=install"), // URL для установки
    'ADMIN_EXTENSIONS_UPDATE_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=update"), // URL для обновления
    'ADMIN_EXTENSIONS_UNINSTALL_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=uninstall"), // URL для удаления
    'ADMIN_EXTENSIONS_UNINSTALL_CONFIRM_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=uninstall&x={$sys['xk']}"), // URL для подтверждения удаления
    'ADMIN_EXTENSIONS_PAUSE_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=pause"), // URL для приостановки
    'ADMIN_EXTENSIONS_UNPAUSE_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code&b=unpause"), // URL для возобновления
]);

// Если включен режим совместимости (legacy mode)
if (isset(Cot::$cfg['legacyMode']) && Cot::$cfg['legacyMode']) {
    $t->assign([
        // @deprecated Для обратной совместимости. Будет удалено в будущих версиях
        'ADMIN_EXTENSIONS_ICO' => $params['legacyIcon'], // Иконка для устаревших версий

        /** @deprecated in 0.9.26 */
        'ADMIN_EXTENSIONS_JUMPTO_URL_TOOLS' => $extensionsService->getAdminPageUrl($code, $type), // URL для инструментов расширения (устаревший)
        'ADMIN_EXTENSIONS_JUMPTO_URL_STRUCT' => $structurePartUrl, // URL для структуры расширения (устаревший)
    ]);
}

 

if ($exists) { // Проверяем, существует ли расширение
    // Tags for existing exts
    $t->assign([ // Присваиваем значения переменным для отображения информации о расширении
        'ADMIN_EXTENSIONS_RIGHTS_URL' => $extensionsService->getRightsUrl($code, $type), // URL для прав доступа
        'ADMIN_EXTENSIONS_ADMRIGHTS_AUTH_GUESTS' => cot_auth_getmask($info['Auth_guests']), // Маска прав для гостей
        'ADMIN_EXTENSIONS_AUTH_GUESTS' => $info['Auth_guests'], // Правила доступа для гостей
        'ADMIN_EXTENSIONS_ADMRIGHTS_LOCK_GUESTS' => cot_auth_getmask($info['Lock_guests']), // Блокировка доступа для гостей
        'ADMIN_EXTENSIONS_LOCK_GUESTS' => $info['Lock_guests'], // Блокировка доступа для гостей
        'ADMIN_EXTENSIONS_ADMRIGHTS_AUTH_MEMBERS' => cot_auth_getmask($info['Auth_members']), // Маска прав для зарегистрированных пользователей
        'ADMIN_EXTENSIONS_AUTH_MEMBERS' => $info['Auth_members'], // Правила доступа для пользователей
        'ADMIN_EXTENSIONS_ADMRIGHTS_LOCK_MEMBERS' => cot_auth_getmask($info['Lock_members']), // Блокировка доступа для пользователей
        'ADMIN_EXTENSIONS_LOCK_MEMBERS' => $info['Lock_members'], // Блокировка доступа для пользователей
        'ADMIN_EXTENSIONS_AUTHOR' => $info['Author'], // Автор расширения
        'ADMIN_EXTENSIONS_COPYRIGHT' => $info['Copyright'], // Авторские права
    ]);

    // Check and display dependencies
    $dependencies_satisfied = true; // Инициализируем переменную, которая будет отслеживать состояние зависимостей
    foreach (['Requires_modules', 'Requires_plugins', 'Recommends_modules', 'Recommends_plugins'] as $dep_type) { // Перебираем типы зависимостей
        if (!empty($info[$dep_type])) { // Проверяем, есть ли зависимости для текущего типа
            $dep_obligatory = strpos($dep_type, 'Requires') === 0; // Проверяем, обязательная ли зависимость
            $dep_module = strpos($dep_type, 'modules') !== false; // Проверяем, является ли зависимость модулем
            $arg = $dep_module ? 'mod' : 'pl'; // Устанавливаем аргумент в зависимости от типа зависимости
            $dir = $dep_module ? Cot::$cfg['modules_dir'] : Cot::$cfg['plugins_dir']; // Устанавливаем директорию в зависимости от типа зависимости

            foreach (explode(',', $info[$dep_type]) as $ext) { // Перебираем все зависимости для текущего типа
                $ext = trim($ext); // Убираем лишние пробелы
                $dep_installed = $extensionsService->isInstalled($ext, null, true); // Проверяем, установлено ли зависимое расширение
                if ($dep_obligatory) { // Если зависимость обязательная
                    $dep_class = $dep_installed ? 'highlight_green' : 'highlight_red'; // Устанавливаем класс для отображения зависимости
                    $dependencies_satisfied &= $dep_installed; // Если зависимость обязательная, проверяем, удовлетворена ли она
                } else {
                    $dep_class = ''; // Для необязательных зависимостей класс не устанавливаем
                }

                $dep_ext_info = $dir . '/' . $ext . '/' . $ext . '.setup.php'; // Путь к файлу настройки расширения
                if (file_exists($dep_ext_info)) { // Если файл настройки существует
                    $dep_info = cot_infoget($dep_ext_info, 'COT_EXT'); // Получаем информацию о расширении
                    if (!$dep_info && cot_plugin_active('genoa')) { // Если не удалось получить информацию, но активен плагин genoa
                        // Try to load old format info
                        $dep_info = cot_infoget($dep_ext_info, 'SED_EXTPLUGIN'); // Пробуем загрузить информацию в старом формате
                    }
                } else {
                    $dep_info = array( // Если файл настройки не существует, создаем массив с названием расширения
                        'Name' => $ext
                    );
                }

                $dependencyUrl = ''; // Инициализируем URL зависимости
                if (
                    ($dep_module && file_exists(Cot::$cfg['modules_dir'] . '/' . $ext)) // Если зависимость является модулем и он существует
                    || (!$dep_module && file_exists(Cot::$cfg['plugins_dir'] . '/' . $ext)) // Если зависимость является плагином и он существует
                ) {
                    $dependencyUrl = cot_url('admin', ['m' => 'extensions', 'a' => 'details', $arg => $ext]); // Получаем URL для просмотра информации о зависимости
                }

                $t->assign([ // Присваиваем значения для отображения информации о зависимости
                    'ADMIN_EXTENSIONS_DEPENDENCIES_ROW_CODE' => $ext, // Код зависимости
                    'ADMIN_EXTENSIONS_DEPENDENCIES_ROW_NAME' => $dep_info['Name'], // Имя зависимости
                    'ADMIN_EXTENSIONS_DEPENDENCIES_ROW_URL' => $dependencyUrl, // URL зависимости
                    'ADMIN_EXTENSIONS_DEPENDENCIES_ROW_TYPE' => $dep_module ? Cot::$L['Module'] : Cot::$L['Plugin'], // Тип зависимости (модуль или плагин)
                    'ADMIN_EXTENSIONS_DEPENDENCIES_ROW_CLASS' => $dep_class, // Класс для отображения зависимости
                ]);
                $t->parse('MAIN.DETAILS.DEPENDENCIES.DEPENDENCIES_ROW'); // Рендерим строку с зависимостью
            }
            $t->assign(array(
                'ADMIN_EXTENSIONS_DEPENDENCIES_TITLE' => Cot::$L['ext_' . strtolower($dep_type)] // Присваиваем заголовок для зависимостей
            ));
            $t->parse('MAIN.DETAILS.DEPENDENCIES'); // Рендерим блок с зависимостями
        }
    }
}

 /* === Hook  === */
foreach (cot_getextplugins('admin.extensions.details') as $pl) { // Подключаем все плагины, которые зарегистрированы для хука 'admin.extensions.details'
    include $pl; // Включаем каждый плагин
}
/* ===== */

$t->parse('MAIN.DETAILS'); // Рендерим блок деталей расширения
break; // Завершаем выполнение текущего case в switch

 

/* =============== */
case 'hooks': // Обрабатываем случай, когда выбран раздел "hooks" (хуки)
/* =============== */
    $adminPath[] = array(cot_url('admin', 'm=extensions&a=hooks'), Cot::$L['Hooks']); // Добавляем путь к разделу хуков в навигацию

    $sql = $db->query("SELECT * FROM $db_plugins ORDER BY pl_hook ASC, pl_code ASC, pl_order ASC"); // Выполняем SQL-запрос для получения всех плагинов из базы данных, сортируя их по хук, код плагина и порядок

    while($row = $sql->fetch()) { // Для каждой строки результата запроса
        $t->assign(array( // Присваиваем значения для отображения информации о плагинах
            'ADMIN_EXTENSIONS_HOOK' => $row['pl_hook'], // Хук плагина
            'ADMIN_EXTENSIONS_CODE' => $row['pl_code'], // Код плагина
            'ADMIN_EXTENSIONS_ORDER' => $row['pl_order'], // Порядок плагина
            'ADMIN_EXTENSIONS_ACTIVE' => $cot_yesno[$row['pl_active']] // Статус активности плагина (Да/Нет)
        ));
        $t->parse('MAIN.HOOKS.HOOKS_ROW'); // Рендерим строку с информацией о плагине
    }
    $sql->closeCursor(); // Закрываем курсор SQL-запроса

    $t->assign(array( // Присваиваем количество плагинов в базе данных
        'ADMIN_EXTENSIONS_CNT_HOOK' => $sql->rowCount() // Количество строк в результате запроса
    ));
    $t->parse('MAIN.HOOKS'); // Рендерим блок с хуками
break; // Завершаем выполнение текущего case в switch

 

/* =============== */
default: // Обрабатываем случай по умолчанию, если ни одно из условий в switch не выполнено
/* =============== */
    // Params to show only installed extensions
    $only_installed = cot_import('inst', 'G', 'BOL'); // Получаем параметр 'inst' из GET-запроса, который определяет, показывать ли только установленные расширения
    if (Cot::$cfg['default_show_installed']) { // Если настройка по умолчанию предполагает показ только установленных расширений
        if (is_null($only_installed)) { // Если параметр 'inst' не был передан
            $only_installed = true; // По умолчанию показываем только установленные расширения
        }
        $only_installed_urlp = $only_installed ? '' : '&inst=0'; // Формируем URL для отображения установленных или неустановленных расширений
        $only_installed_toggle = $only_installed ? '&inst=0' : ''; // Формируем URL для переключения между установленными и неустановленными расширениями
    } else { // Если настройка по умолчанию не предполагает показ только установленных расширений
        $only_installed_urlp = $only_installed ? '&inst=1' : ''; // Формируем URL для отображения установленных или неустановленных расширений
        $only_installed_toggle = $only_installed ? '' : '&inst=1'; // Формируем URL для переключения между установленными и неустановленными расширениями
    }
    $sort_urlp = $sort == 'cat' ? '&sort=cat' : ''; // Формируем URL для сортировки по категориям

    // Filter/sort tags
    $t->assign([ // Присваиваем значения для отображения фильтров и сортировки расширений
        'ADMIN_EXTENSIONS_HOOKS_URL' => cot_url('admin', 'm=extensions&a=hooks'), // URL для перехода в раздел хуков
        'ADMIN_EXTENSIONS_SORT_ALP_URL' => cot_url('admin', 'm=extensions'.$only_installed_urlp), // URL для сортировки по алфавиту
        'ADMIN_EXTENSIONS_SORT_ALP_SEL' => $sort != 'cat', // Определяем, выбран ли режим сортировки по алфавиту
        'ADMIN_EXTENSIONS_SORT_CAT_URL' => cot_url('admin', 'm=extensions&sort=cat'.$only_installed_urlp), // URL для сортировки по категориям
        'ADMIN_EXTENSIONS_SORT_CAT_SEL' => $sort == 'cat', // Определяем, выбран ли режим сортировки по категориям
        'ADMIN_EXTENSIONS_ALL_EXTENSIONS_URL' => cot_url('admin', 'm=extensions'.$sort_urlp), // URL для отображения всех расширений
        'ADMIN_EXTENSIONS_ONLY_INSTALLED_URL' => cot_url('admin', 'm=extensions'.$sort_urlp.$only_installed_toggle), // URL для отображения только установленных расширений
        'ADMIN_EXTENSIONS_ONLY_INSTALLED_SEL' => $only_installed, // Определяем, показывать ли только установленные расширения
    ]);

 

// Prefetch common data to save SQL queries // Предзагружаем общие данные, чтобы сократить количество SQL-запросов
$totalConfigs = []; // Инициализируем массив для хранения информации о конфигурациях
$sql = Cot::$db->query( // Выполняем SQL-запрос для получения количества конфигураций по владельцу и категории
    'SELECT COUNT(*) AS cnt, config_owner, config_cat FROM ' . Cot::$db->config // Запрашиваем количество конфигураций, владельца и категорию
    . ' WHERE config_type NOT IN (' . COT_CONFIG_TYPE_HIDDEN . ', ' . COT_CONFIG_TYPE_SEPARATOR . ') ' // Исключаем скрытые и разделительные конфигурации
    . "AND (config_subcat IN ('', '__default') OR config_subcat IS NULL) " // Оставляем только те, которые принадлежат основной категории или не имеют подкатегории
    . 'GROUP BY config_owner, config_cat'); // Группируем по владельцу и категории конфигураций

while ($row = $sql->fetch()) { // Перебираем результаты запроса
    $totalConfigs[$row['config_owner']][$row['config_cat']] = (int) $row['cnt']; // Сохраняем количество конфигураций для каждого владельца и категории
}
$sql->closeCursor(); // Закрываем курсор SQL-запроса

$totalactives = []; // Инициализируем массив для хранения информации о количестве активных плагинов
$totalinstalleds = []; // Инициализируем массив для хранения информации о количестве установленных плагинов
foreach ($db->query("SELECT SUM(pl_active) AS sum, COUNT(*) AS cnt, pl_code FROM $db_plugins GROUP BY pl_code")->fetchAll() as $row) { // Выполняем SQL-запрос для получения суммы активных плагинов и их общего количества по коду плагина
    $totalactives[$row['pl_code']] = (int) $row['sum']; // Сохраняем количество активных плагинов для каждого кода плагина
    $totalinstalleds[$row['pl_code']] = (int) $row['cnt']; // Сохраняем количество установленных плагинов для каждого кода плагина
}

$installed_vers = []; // Инициализируем массив для хранения информации о версиях установленных компонентов
foreach($db->query("SELECT ct_version, ct_code FROM $db_core")->fetchAll() as $row) { // Выполняем SQL-запрос для получения версий установленных компонентов
    $installed_vers[$row['ct_code']] = $row['ct_version']; // Сохраняем версию для каждого компонента по его коду
}

 

$extensionsWithStructure = cot_getExtensionsWithStructure(); // Получаем список расширений с структурой

foreach (['module', 'plug'] as $type) { // Перебираем типы расширений: 'module' и 'plug'
    $sql = $db->query("SELECT DISTINCT(config_cat), COUNT(*) FROM $db_config // Выполняем SQL-запрос для получения уникальных категорий конфигураций и их количества
        WHERE config_owner='$type' GROUP BY config_cat"); // Где владелец конфигурации соответствует текущему типу расширений
    while ($row = $sql->fetch(PDO::FETCH_NUM)) { // Перебираем результаты запроса
        $cfgentries[$row[0]] = $row[1]; // Сохраняем количество конфигураций для каждой категории
    }
    $sql->closeCursor(); // Закрываем курсор SQL-запроса

    $dir = $type == 'module' ? Cot::$cfg['modules_dir'] : Cot::$cfg['plugins_dir']; // Определяем директорию в зависимости от типа расширения
    $extensions = cot_extension_list_info($dir); // Получаем информацию о расширениях из соответствующей директории
    $ctplug = $type == 'module' ? '0' : '1'; // Устанавливаем значение для типа расширения: 0 для модуля, 1 для плагина

 

if ($only_installed) {  // Если нужно фильтровать только установленные расширения
    // Filter only installed exts
    $tmp = [];  // Создаем временный массив для хранения установленных расширений
    $installed_exts = $db->query("SELECT ct_code FROM $db_core WHERE ct_plug = $ctplug")->fetchAll(PDO::FETCH_COLUMN);  // Получаем список установленных расширений для данного типа (модуль или плагин)
    foreach ($extensions as $key => $val) {  // Перебираем все расширения
        if (in_array($key, $installed_exts)) {  // Если расширение установлено
            $tmp[$key] = $val;  // Добавляем его в временный массив
        }
    }
    $extensions = $tmp;  // Перезаписываем оригинальный массив установленных расширений
}

// Find missing extensions
$extlist = count($extensions) > 0 ? "ct_code NOT IN('" . implode("','", array_keys($extensions)) . "')" : '1';  // Формируем SQL-условие для поиска отсутствующих расширений
$sql = $db->query("SELECT * FROM $db_core WHERE $extlist AND ct_plug = $ctplug");  // Выполняем запрос к базе данных для поиска расширений, которых нет в списке
foreach ($sql->fetchAll() as $row) {  // Перебираем результаты запроса
    if ($type ==  'module' && in_array($row['ct_code'], array('admin', 'message', 'users'))) {  // Если тип расширения — модуль и это одно из исключений (admin, message, users)
        continue;  // Пропускаем эти расширения
    }
    $extensions[$row['ct_code']] = [  // Добавляем расширение в массив
        'Code' => $row['ct_code'],  // Код расширения
        'Name' => $row['ct_title'],  // Название расширения
        'Version' => $row['ct_version'],  // Версия расширения
        'Category' => 'misc-ext',  // Категория расширения
        'NotFound' => true,  // Флаг, что расширение не найдено
    ];
}

 

if ($type == 'plug' && $sort == 'cat') {  // Если тип расширения — плагин и сортировка по категории
    uasort($extensions, 'cot_extension_catcmp');  // Сортируем расширения по категории с использованием функции сравнения cot_extension_catcmp
} else {  // Если сортировка не по категории
    ksort($extensions);  // Сортируем расширения по ключам (по коду расширения)
}

$cnt_extp = count($extensions);  // Получаем количество расширений
$cnt_parts = 0;  // Инициализируем переменную для подсчета частей (разделов)

$prev_cat = '';  // Инициализируем переменную для хранения предыдущей категории

 

/* === Hook - Part1 : Set === */ // Заголовок, указывающий на начало первого этапа хука для плагинов
$extp = cot_getextplugins("admin.extensions.$type.list.loop"); // Получение плагинов для указанного типа расширений из хука
/* ===== */ // Завершение первого этапа хука

$i = 1; // Инициализация счетчика для перебора расширений
foreach ($extensions as $code => $info) { // Начало цикла по всем расширениям
    if ( // Условие для сортировки по категории, только если сортировка установлена на 'cat' и тип расширения - плагин
        $sort === 'cat' 
        && $type === ExtensionsDictionary::TYPE_PLUGIN 
        && $prev_cat !== $info['Category'] // Проверка, если категория отличается от предыдущей
    ) {
        // Рендеринг заголовка категории
        $t->assign(
            'ADMIN_EXTENSIONS_CAT_TITLE', // Название категории для отображения
            Cot::$L['ext_cat_' . $info['Category']] ?? $info['Category'] // Получение перевода категории, если он существует, иначе используем саму категорию
        );
        $t->parse('MAIN.DEFAULT.SECTION.ROW.ROW_CAT'); // Парсинг шаблона для вывода категории
        // Присваиваем новую категорию в переменную для дальнейшей проверки
        $prev_cat = $info['Category']; // Обновление переменной $prev_cat с текущей категорией
    }

 

$exists = !isset($info['NotFound']); // Проверка, существует ли ключ 'NotFound' в информации о расширении, определяет, найдено ли расширение
$isInstalled = $extensionsService->isInstalled($code, $type, true); // Проверка, установлено ли расширение

if (!empty($info['Error'])) { // Если существует ошибка в информации о расширении
    $t->assign([ // Присваиваем данные для отображения ошибки
        'ADMIN_EXTENSIONS_X_ERR' => $code, // Код расширения с ошибкой
        'ADMIN_EXTENSIONS_ERROR_MSG' => $info['Error'], // Сообщение об ошибке
    ]);
    $t->parse('MAIN.DEFAULT.ROW.ROW_ERROR_EXT'); // Парсинг шаблона для вывода ошибки
    $t->parse('MAIN.DEFAULT.ROW'); // Парсинг строки для вывода расширения с ошибкой

} else { // Если ошибок нет
    $totalactive = isset($totalactives[$code]) ? $totalactives[$code] : 0; // Получаем количество активных экземпляров расширения, если оно есть
    $totalinstalled = isset($totalinstalleds[$code]) ? $totalinstalleds[$code] : 0; // Получаем количество установленных экземпляров расширения, если оно есть

    $cnt_parts += $totalinstalled; // Увеличиваем общий счетчик установленных расширений

    if (!isset($installed_vers[$code])) { // Если версия расширения не установлена
        $part_status = 3; // Статус части - 3, что означает отсутствие версии
        $info['Partscount'] = '?'; // Количество частей для этого расширения неизвестно

    } else { // Если версия расширения установлена
        $info['Partscount'] = $totalinstalled; // Устанавливаем количество частей как количество установленных экземпляров
        if (!$exists) { // Если расширение не найдено
            $part_status = 4; // Статус части - 4, что означает, что расширение отсутствует
        } elseif ($totalinstalled > $totalactive && $totalactive > 0) { // Если установлено больше экземпляров, чем активно, и есть активные экземпляры
            $part_status = 2; // Статус части - 2, что означает частичную активацию
        } elseif ($totalactive == 0 && $totalinstalled > 0) { // Если нет активных экземпляров, но есть установленные
            $part_status = 0; // Статус части - 0, что означает, что расширение не активно
        } else { // В остальных случаях
            $part_status = 1; // Статус части - 1, что означает полную активацию
        }
    }

    $totalConfig = !empty($totalConfigs[$type][$code]) ? $totalConfigs[$type][$code] : 0; // Получаем количество конфигураций для расширения

    $ent_code = isset($cfgentries[$code]) ? $cfgentries[$code] : 0; // Получаем количество записей конфигурации для расширения

    $hasStructure = in_array($code, $extensionsWithStructure, true); // Проверяем, имеет ли расширение структуру, используя строгую проверку

    if ($type === ExtensionsDictionary::TYPE_MODULE) { // Если тип расширения - модуль
        $arg = 'mod'; // Устанавливаем аргумент как 'mod' для модуля
    } else { // Если тип расширения - плагин
        $arg = 'pl'; // Устанавливаем аргумент как 'pl' для плагина
    }

 

$installed_ver = $installed_vers[$code] ?? ''; // Получаем установленную версию расширения, если она существует, иначе пустая строка

$params = cot_get_extensionparams($code, $type === ExtensionsDictionary::TYPE_MODULE); // Получаем параметры расширения (имя, описание и другие данные)

$rightsUrl = null; // Инициализируем переменную для URL прав доступа

Cot::$L['info_name'] = Cot::$L['info_desc'] = Cot::$L['info_notes'] = ''; // Очищаем значения переменных для имени, описания и заметок

$langFile = cot_langfile($code, $type); // Получаем путь к языковому файлу расширения
if (!empty($langFile) ?? file_exists($langFile)) { // Если языковой файл существует
    include $langFile; // Подключаем языковой файл
}

$t->assign([ // Присваиваем переменные для отображения информации о расширении
    'ADMIN_EXTENSIONS_DETAILS_URL' => cot_url('admin', "m=extensions&a=details&$arg=$code"), // URL для страницы деталей расширения
    'ADMIN_EXTENSIONS_NAME' => htmlspecialchars($params['name']), // Имя расширения
    'ADMIN_EXTENSIONS_TYPE' => $type === ExtensionsDictionary::TYPE_MODULE
        ? Cot::$L['Module'] // Если это модуль, то выводим "Module"
        : Cot::$L['Plugin'], // Если это плагин, то выводим "Plugin"
    'ADMIN_EXTENSIONS_CODE_X' => $code, // Код расширения
    'ADMIN_EXTENSIONS_NOTES' => $params['notes'], // Заметки по расширению
    'ADMIN_EXTENSIONS_DESCRIPTION' => $params['desc'], // Описание расширения
    'ADMIN_EXTENSIONS_ICON' => $params['icon'], // Иконка расширения
    'ADMIN_EXTENSIONS_EDIT_URL' => cot_url('admin', "m=config&n=edit&o=$type&p=$code"), // URL для редактирования расширения
    'ADMIN_EXTENSIONS_TOTALCONFIG' => $totalConfig, // Общее количество конфигураций для расширения
    'ADMIN_EXTENSIONS_PARTSCOUNT' => $info['Partscount'], // Количество частей расширения
    'ADMIN_EXTENSIONS_STATUS' => $status[$part_status], // Статус расширения
    'ADMIN_EXTENSIONS_VERSION' => $info['Version'], // Версия расширения
    'ADMIN_EXTENSIONS_VERSION_INSTALLED' => $installed_ver, // Установленная версия расширения
    'ADMIN_EXTENSIONS_VERSION_COMPARE' => version_compare($info['Version'], $installed_ver), // Сравнение версий расширения
    'ADMIN_EXTENSIONS_RIGHTS_URL' => $extensionsService->getRightsUrl($code, $type), // URL для прав доступа к расширению
    'ADMIN_EXTENSIONS_ADMIN_URL' => $extensionsService->getAdminPageUrl($code, $type), // URL для административной страницы расширения
    'ADMIN_EXTENSIONS_JUMPTO_URL' => $extensionsService->getPublicPageUrl($code, $type), // URL для публичной страницы расширения
    'ADMIN_EXTENSIONS_STRUCTURE_URL' => $hasStructure
        ? cot_url('admin', ['m' => 'structure', 'n' => $code]) // URL для структуры, если она существует
        : '', 
    //'ADMIN_EXTENSIONS_ODDEVEN' => cot_build_oddeven($i), // Пример для парности, не используется в коде
]);

if (isset(Cot::$cfg['legacyMode']) && Cot::$cfg['legacyMode']) { // Если включен режим совместимости
    $t->assign([ // Присваиваем старые данные для совместимости
        'ADMIN_EXTENSIONS_ICO' => $params['legacyIcon'], // Старый иконка расширения для совместимости

        /** @deprecated in 0.9.26 */
        'ADMIN_EXTENSIONS_JUMPTO_URL_TOOLS' => $extensionsService->getAdminPageUrl($code, $type), // Старый URL для инструментов расширения
        'ADMIN_EXTENSIONS_JUMPTO_URL_STRUCT' => $hasStructure
            ? cot_url('admin', ['m' => 'structure', 'n' => $code]) // Старый URL для структуры расширения
            : '',
    ]);
}

/* === Hook - Part2 : Include === */
foreach ($extp as $pl) { // Для каждого плагина в хуке
    include $pl; // Включаем плагин
}
/* ===== */
$t->parse('MAIN.DEFAULT.SECTION.ROW'); // Парсинг строки для отображения данных о расширении

$i++; // Увеличиваем счетчик для следующих итераций

 

$t->assign(array( // Присваиваем переменные для отображения данных о расширении
    'ADMIN_EXTENSIONS_SECTION_TITLE' => $type == 'module' ? Cot::$L['Modules'] : Cot::$L['Plugins'], // Заголовок секции: если это модуль, то "Modules", иначе "Plugins"
    'ADMIN_EXTENSIONS_CNT_EXTP' => $cnt_extp // Количество расширений
));
$t->parse('MAIN.DEFAULT.SECTION'); // Парсинг секции расширений

}

$t->parse('MAIN.DEFAULT'); // Парсинг основной секции

break; // Завершаем выполнение текущего кейса в switch

cot_display_messages($t); // Отображаем сообщения (если есть) через шаблон

/* === Hook  === */
foreach (cot_getextplugins('admin.extensions.tags') as $pl) { // Для каждого плагина в хуке 'admin.extensions.tags'
    include $pl; // Включаем плагин
}
/* ===== */

$t->parse('MAIN'); // Парсинг основного шаблона

$adminMain = $t->text('MAIN'); // Получаем HTML-контент основного шаблона и сохраняем в переменную

 

 

40 минут чтения Administrator

Комментарии (0)

Комментарии отсутствуют
Добавление комментариев доступно только зарегистрированным пользователям

Автор контента

Administrator

Оффлайн

Administrator

Последняя авторизация: 05.07.2026 18:32

  • Страница размещена: 28.01.2025 16:16
  • Последнее обновление: 28.01.2025 17:21

Похожие страницы

Файл extensions.php в Cotonti
1 Файл extensions.php в системной папке Cotonti CMF имеет основное назначение управлять расширениями системы. В
Файл extensions.php в Cotonti
2 Файл "extensions.php" в системе Cotonti CMF выполняет роль конфигурационного файла, содержащего список
Файл admin.cache.disk.php в Cotonti CMF
3 Файл admin.cache.disk.php в движке Cotonti CMF является частью административной панели, предназначенной для управления
Файл admin.config.php в Cotonti CMF
4 Основное назначение файла и его место в системе Файл admin.config.php является частью административной панели системы
Файл admin.extrafields.php в Cotonti CMF
5 Файл admin.extrafields.php в движке Cotonti CMF предназначен для управления дополнительными полями (extra fields) в