Files
2026-07-30 11:56:32 +03:00

295 lines
12 KiB
PHP

<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file adminx/modules/Modules/Controller.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Adminx\Modules;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Common\AdminAssets;
use App\Common\AuditLog;
use App\Common\Auth;
use App\Common\Controller as BaseController;
use App\Common\Permission;
use App\Helpers\Request;
class Controller extends BaseController
{
public function index(array $params = array())
{
AdminAssets::addStyle($this->base() . '/modules/Modules/assets/modules.css', 50);
AdminAssets::addScript($this->base() . '/modules/Modules/assets/modules.js', 50);
try {
$result = array(
'items' => NativeModuleAdapter::all(),
'errors' => NativeModuleAdapter::errors(),
);
$fatal = '';
} catch (\Throwable $e) {
$result = array('items' => array(), 'errors' => array());
$fatal = $e->getMessage();
}
$repository = ModuleRepository::catalog(false);
return $this->render('@modules/index.twig', array(
'modules' => $result['items'],
'errors' => $result['errors'],
'fatal' => $fatal,
'stats' => NativeModuleAdapter::stats($result['items']),
'can_manage' => Permission::check('manage_modules'),
'can_install_code' => Permission::check('install_module_code'),
'repository' => $repository,
'repository_settings' => ModuleRepository::settings(),
'official_repository' => ModuleRepository::officialSettings(),
'focus_code' => strtolower(trim(Request::getStr('focus', ''))),
));
}
public function toggle(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
$enabled = Request::postBool('enabled', false);
return $this->perform($params, $enabled ? 'module.enabled' : 'module.disabled', function ($code) use ($enabled) {
return $this->moduleAction($code, 'toggle', array($enabled));
}, $enabled ? 'Модуль включён' : 'Модуль отключён');
}
public function install(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
return $this->perform($params, 'module.installed', function ($code) { return $this->moduleAction($code, 'install'); }, 'Модуль установлен');
}
public function update(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
return $this->perform($params, 'module.updated', function ($code) { return $this->moduleAction($code, 'update'); }, 'Миграции модуля применены');
}
public function reinstall(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
$token = Request::postStr('preflight_token', '');
return $this->perform($params, 'module.reinstalled', function ($code) use ($token) { return $this->moduleAction($code, 'reinstall', array($token)); }, 'Модуль переустановлен');
}
public function repair(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
return $this->perform($params, 'module.repaired', function ($code) { return $this->moduleAction($code, 'repair'); }, 'Операция модуля восстановлена');
}
public function uninstall(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
$token = Request::postStr('preflight_token', '');
return $this->perform($params, 'module.uninstalled', function ($code) use ($token) { return $this->moduleAction($code, 'uninstall', array($token)); }, 'Модуль деинсталлирован');
}
public function preflight(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
$code = isset($params['code']) ? (string) $params['code'] : '';
$operation = isset($params['operation']) ? (string) $params['operation'] : '';
try {
$result = NativeModuleAdapter::preflight($code, $operation);
return $this->success('Предварительная проверка выполнена', array('data' => $result));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
public function remove(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
return $this->perform($params, 'module.files_removed', function ($code) {
return NativeModuleAdapter::remove($code);
}, 'Файлы модуля удалены');
}
public function presentation(array $params = array())
{
if (($guard = $this->guard()) !== null) { return $guard; }
$code = isset($params['code']) ? (string) $params['code'] : '';
try {
$item = NativeModuleAdapter::savePresentation(
$code,
Request::postBool('header_enabled', false),
Request::postBool('dashboard_enabled', false)
);
$this->auditPresentation($item);
return $this->success('Размещение модуля сохранено', array('data' => array('module' => $item)));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
public function installArchive(array $params = array())
{
if (($guard = $this->guard('manage_modules', 'install_module_code')) !== null) { return $guard; }
try {
$file = isset($_FILES['module_archive']) && is_array($_FILES['module_archive'])
? $_FILES['module_archive']
: array();
$item = ModuleArchiveInstaller::install($file);
$user = Auth::user();
AuditLog::record('module.archive_installed', array(
'actor_id' => Auth::id(),
'actor_name' => $user && isset($user['name']) ? (string) $user['name'] : '',
'target_type' => 'native_module',
'target_id' => null,
'meta' => array(
'code' => $item['code'],
'version' => $item['version'],
'folder' => $item['folder'],
'manifest_sha256' => isset($item['manifest_sha256']) ? $item['manifest_sha256'] : '',
),
));
$adminUrl = isset($item['admin_url']) ? trim((string) $item['admin_url']) : '';
$redirect = $adminUrl !== '' && strpos($adminUrl, '/') === 0
? $this->base() . $adminUrl
: $this->base() . '/modules';
return $this->success('Модуль «' . $item['name'] . '» установлен', array(
'data' => array('module' => $item),
'redirect' => $redirect,
));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
public function saveRepositorySettings(array $params = array())
{
if (($guard = $this->guard('manage_modules', 'install_module_code')) !== null) { return $guard; }
try {
$settings = ModuleRepository::saveSettings(array(
'enabled' => Request::postBool('enabled', false),
'url' => Request::postStr('url', ''),
'public_key' => Request::postStr('public_key', ''),
));
$this->auditRepository('module.repository_settings_updated', array(
'enabled' => !empty($settings['enabled']),
'url' => isset($settings['url']) ? $settings['url'] : '',
));
return $this->success('Настройки каталога сохранены', array('data' => array('settings' => $settings)));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
public function restoreOfficialRepository(array $params = array())
{
if (($guard = $this->guard('manage_modules', 'install_module_code')) !== null) { return $guard; }
try {
$settings = ModuleRepository::restoreOfficial();
$this->auditRepository('module.repository_official_restored', array('url' => $settings['url']));
return $this->success('Официальный каталог модулей восстановлен', array('data' => array('settings' => $settings)));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
public function refreshRepository(array $params = array())
{
if (($guard = $this->guard('view_modules')) !== null) { return $guard; }
$catalog = ModuleRepository::catalog(true);
if (!empty($catalog['error'])) { return $this->error($catalog['error'], array(), 422); }
return $this->success('Каталог модулей обновлён', array('data' => array('catalog' => $catalog)));
}
public function installRepository(array $params = array())
{
if (($guard = $this->guard('manage_modules', 'install_module_code')) !== null) { return $guard; }
$code = isset($params['code']) ? (string) $params['code'] : '';
try {
$item = ModuleRepository::install($code);
$updated = isset($item['repository_operation']) && $item['repository_operation'] === 'update';
$this->auditRepository($updated ? 'module.repository_updated' : 'module.repository_installed', array(
'code' => $code,
'version' => isset($item['version']) ? $item['version'] : (isset($item['file_version']) ? $item['file_version'] : ''),
'manifest_sha256' => isset($item['manifest_sha256']) ? $item['manifest_sha256'] : '',
));
$adminUrl = isset($item['admin_url']) ? trim((string) $item['admin_url']) : '';
$redirect = $adminUrl !== '' && strpos($adminUrl, '/') === 0
? $this->base() . $adminUrl
: $this->base() . '/modules';
return $this->success('Модуль «' . (isset($item['name']) ? $item['name'] : $code) . '» '
. ($updated ? 'обновлён из каталога' : 'установлен из каталога'), array(
'data' => array('module' => $item),
'redirect' => $updated ? $this->base() . '/modules?tab=catalog' : $redirect,
));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
protected function perform(array $params, $action, $callback, $message)
{
$code = isset($params['code']) ? (string) $params['code'] : '';
try {
$item = call_user_func($callback, $code);
$user = Auth::user();
AuditLog::record($action, array(
'actor_id' => Auth::id(),
'actor_name' => $user && isset($user['name']) ? (string) $user['name'] : '',
'target_type' => 'native_module',
'target_id' => !empty($item['id']) ? (int) $item['id'] : null,
'meta' => array('code' => $code, 'version' => isset($item['file_version']) ? $item['file_version'] : ''),
));
return $this->success($message, array('data' => array('module' => $item)));
} catch (\Throwable $e) {
return $this->error($e->getMessage(), array(), 422);
}
}
protected function moduleAction($code, $method, array $arguments = array())
{
return call_user_func_array(array(NativeModuleAdapter::class, $method), array_merge(array($code), $arguments));
}
protected function auditPresentation(array $item)
{
$user = Auth::user();
AuditLog::record('module.presentation_updated', array(
'actor_id' => Auth::id(),
'actor_name' => $user && isset($user['name']) ? (string) $user['name'] : '',
'target_type' => 'native_module',
'target_id' => null,
'meta' => array(
'code' => $item['code'],
'header_enabled' => !empty($item['header_enabled']),
'dashboard_enabled' => !empty($item['dashboard_enabled']),
),
));
}
protected function auditRepository($action, array $meta)
{
$user = Auth::user();
AuditLog::record($action, array(
'actor_id' => Auth::id(),
'actor_name' => $user && isset($user['name']) ? (string) $user['name'] : '',
'target_type' => 'module_repository',
'target_id' => null,
'meta' => $meta,
));
}
protected function guard($permission = 'manage_modules', $additionalPermission = '')
{
return $this->guardPermission($permission, $additionalPermission);
}
}