| @copyright 2007-2026 (c) AVE.cms | @link https://ave-cms.ru | @version 3.3 */ namespace App\Adminx\Media; defined('BASEPATH') || die('Direct access to this location is not allowed.'); use App\Common\AdminAssets; use App\Common\Auth; use App\Common\Controller as BaseController; use App\Common\Permission; use App\Content\Documents\DocumentMediaDraft; use App\Helpers\Request; class Controller extends BaseController { public function index(array $params = array()) { AdminAssets::addStyle($this->base() . '/modules/Media/assets/media.css', 50); AdminAssets::addScript($this->base() . '/modules/Media/assets/media.js', 50); $dir = Model::normalize(Request::getStr('dir', Model::ROOT), Model::ROOT); $q = trim(Request::getStr('q', '')); $type = Request::getStr('type', ''); $type = in_array($type, array('image', 'file'), true) ? $type : ''; $view = Request::getStr('view', 'grid') === 'table' ? 'table' : 'grid'; $page = max(1, Request::getInt('page', 1)); $perPage = 48; $listing = Model::listing($dir, $q, $type, $page, $perPage); return $this->render('@media/index.twig', array( 'title' => 'Медиа', 'dir' => $listing['dir'], 'breadcrumbs' => Model::breadcrumbs($listing['dir']), 'parent_dir' => Model::parentDir($listing['dir']), 'folders' => $listing['folders'], 'files' => $listing['files'], 'stats' => $listing['stats'], 'filters' => array('q' => $q, 'type' => $type, 'view' => $view), 'page' => $listing['page'], 'pages' => $listing['pages'], 'total' => $listing['total'], 'per_page' => $perPage, 'can_manage' => Permission::check('manage_media'), )); } public function file(array $params = array()) { AdminAssets::addStyle($this->base() . '/modules/Media/assets/media.css', 50); AdminAssets::addScript($this->base() . '/modules/Media/assets/media.js', 50); $file = Model::file(Request::getStr('path', '')); if (!$file) { return $this->renderStatus('@adminx/404.twig', array('title' => 'Файл не найден'), 404); } return $this->render('@media/file.twig', array( 'title' => 'Медиа: ' . $file['name'], 'file' => $file, 'parent_dir' => Model::parentDir($file['path']), 'can_manage' => Permission::check('manage_media'), 'supports_webp' => Model::supportsWebp(), 'presets' => ImagePresets::options(), )); } public function picker(array $params = array()) { $dir = Model::normalize(Request::getStr('dir', Model::ROOT), Model::ROOT); $q = trim(Request::getStr('q', '')); $type = Request::getStr('type', 'image'); $type = in_array($type, array('image', 'file', 'all'), true) ? $type : 'image'; $listingType = $type === 'all' ? '' : $type; $page = max(1, Request::getInt('page', 1)); $perPage = Request::getInt('per_page', 36); $perPage = max(12, min(60, $perPage)); $listing = Model::listing($dir, $q, $listingType, $page, $perPage); return $this->success('', array( 'data' => array( 'dir' => $listing['dir'], 'type' => $type, 'parent_dir' => Model::parentDir($listing['dir']), 'breadcrumbs' => Model::breadcrumbs($listing['dir']), 'folders' => $this->pickerFolders($listing['folders']), 'files' => $this->pickerFiles($listing['files']), 'page' => $listing['page'], 'pages' => $listing['pages'], 'total' => $listing['total'], 'per_page' => $perPage, ), )); } public function folderFiles(array $params = array()) { $dir = Model::normalize(Request::getStr('dir', Model::ROOT), Model::ROOT); $type = Request::getStr('type', 'image'); $type = in_array($type, array('image', 'file', 'all'), true) ? $type : 'image'; $limit = Request::getInt('limit', 500); $limit = max(1, min(1000, $limit)); try { $files = Model::folderFiles($dir, $type === 'all' ? '' : $type, $limit); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } return $this->success('', array( 'data' => array( 'dir' => $dir, 'type' => $type, 'files' => $this->pickerFiles($files), 'count' => count($files), ), )); } public function upload(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } if (empty($_FILES['files']) || empty($_FILES['files']['name'])) { return $this->error('Выберите файлы для загрузки'); } try { $draftToken = Request::postStr('media_draft_token', ''); $dir = $draftToken !== '' ? DocumentMediaDraft::uploadDirectory( $draftToken, Request::postInt('field_id', 0), Auth::id(), Request::postInt('document_id', 0) ) : Request::postStr('dir', Model::ROOT); $result = Model::upload($dir, $_FILES['files']); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } if (empty($result['uploaded'])) { return $this->error('Файлы не загружены', array('files' => implode(', ', $result['errors']))); } $message = 'Загружено файлов: ' . count($result['uploaded']); if (!empty($result['errors'])) { $message .= '. Пропущено: ' . count($result['errors']); } return $this->success($message, array( 'data' => array('files' => $result['uploaded']), 'redirect' => $this->base() . '/media?dir=' . rawurlencode($result['dir']), )); } public function createFolder(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } try { $path = Model::createFolder(Request::postStr('dir', Model::ROOT), Request::postStr('name', '')); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } return $this->success('Папка создана', array( 'redirect' => $this->base() . '/media?dir=' . rawurlencode($path), )); } public function rename(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } try { $target = Model::rename(Request::postStr('path', ''), Request::postStr('name', '')); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } $redirect = is_file(Model::abs($target)) ? $this->base() . '/media/file?path=' . rawurlencode($target) : $this->base() . '/media?dir=' . rawurlencode($target); return $this->success('Переименовано', array('redirect' => $redirect)); } public function delete(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $path = Request::postStr('path', ''); $parent = Model::parentDir($path); try { Model::delete($path); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } return $this->success('Удалено', array( 'redirect' => $this->base() . '/media?dir=' . rawurlencode($parent ?: Model::ROOT), )); } public function clearThumbnails(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $dir = Request::postStr('path', Model::ROOT); try { $result = Model::clearThumbnails($dir); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } $message = $result['roots'] > 0 ? 'Удалено папок превью: ' . $result['roots'] . ', файлов: ' . $result['files'] : 'В этой папке превью не найдены'; return $this->success($message, array( 'data' => $result, 'redirect' => $this->base() . '/media?dir=' . rawurlencode($dir), )); } public function transform(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $path = Request::postStr('path', ''); try { $out = Model::transform($path, array( 'mode' => Request::postStr('mode', 'fit'), 'width' => Request::postInt('width', 320), 'height' => Request::postInt('height', 240), 'format' => Request::postStr('format', 'original'), 'quality' => Request::postInt('quality', 82), 'webp_twin' => Request::postInt('webp_twin', 0), 'crop_enabled' => Request::postStr('crop_enabled', ''), 'crop_x' => Request::postInt('crop_x', 0), 'crop_y' => Request::postInt('crop_y', 0), 'crop_w' => Request::postInt('crop_w', 0), 'crop_h' => Request::postInt('crop_h', 0), )); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } return $this->success('Изображение создано', array( 'data' => array('path' => $out), 'redirect' => $this->base() . '/media?dir=' . rawurlencode(Model::parentDir($out)), )); } public function preview(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $path = Request::postStr('path', ''); try { $preview = Model::previewTransform($path, array( 'mode' => Request::postStr('mode', 'fit'), 'width' => Request::postInt('width', 320), 'height' => Request::postInt('height', 240), 'format' => Request::postStr('format', 'original'), 'quality' => Request::postInt('quality', 82), 'crop_enabled' => Request::postStr('crop_enabled', ''), 'crop_x' => Request::postInt('crop_x', 0), 'crop_y' => Request::postInt('crop_y', 0), 'crop_w' => Request::postInt('crop_w', 0), 'crop_h' => Request::postInt('crop_h', 0), )); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } $meta = trim(($preview['width'] && $preview['height'] ? $preview['width'] . 'x' . $preview['height'] : '') . ' · ' . strtoupper($preview['format']) . ($preview['size_label'] !== '' ? ' · ' . $preview['size_label'] : ''), ' ·'); return $this->success('', array('data' => array( 'path' => $preview['path'], 'url' => $preview['path'], 'meta' => $meta, ))); } public function previewClear(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } Model::deletePreview(Request::postStr('path', '')); return $this->success(''); } public function convertWebp(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } try { $out = Model::convertWebp(Request::postStr('path', ''), Request::postInt('quality', 82)); } catch (\RuntimeException $e) { return $this->error($e->getMessage(), array(), 422); } return $this->success('WebP-копия создана', array( 'data' => array('path' => $out), 'redirect' => $this->base() . '/media/file?path=' . rawurlencode($out), )); } public function presets(array $params = array()) { if (!Permission::check('view_media')) { return $this->error('Недостаточно прав', array(), 403); } return $this->success('', array('data' => array( 'items' => ImagePresets::all(), 'modes' => ImagePresets::modes(), 'formats' => ImagePresets::formats(), 'can_manage' => Permission::check('manage_media'), ))); } public function storePreset(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } list($errors, $data) = ImagePresets::validate(Request::postAll(), 0); if (!empty($errors)) { return $this->error('Проверьте поля', $errors, 422); } $id = ImagePresets::save(0, $data); return $this->success('Вид создан', array('data' => array('id' => $id))); } public function updatePreset(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } $id = isset($params['id']) ? (int) $params['id'] : 0; if (!ImagePresets::one($id)) { return $this->error('Вид не найден', array(), 404); } list($errors, $data) = ImagePresets::validate(Request::postAll(), $id); if (!empty($errors)) { return $this->error('Проверьте поля', $errors, 422); } ImagePresets::save($id, $data); return $this->success('Вид сохранён', array('data' => array('id' => $id))); } public function deletePreset(array $params = array()) { if (($err = $this->guard()) !== null) { return $err; } if (!ImagePresets::delete(isset($params['id']) ? (int) $params['id'] : 0)) { return $this->error('Вид не найден', array(), 404); } return $this->success('Вид удалён'); } protected function guard() { return $this->guardPermission('manage_media'); } protected function pickerFolders(array $folders) { $out = array(); foreach ($folders as $folder) { $out[] = array( 'name' => isset($folder['name']) ? $folder['name'] : '', 'path' => isset($folder['path']) ? $folder['path'] : '', 'parent' => isset($folder['parent']) ? $folder['parent'] : '', 'count' => isset($folder['count']) ? (int) $folder['count'] : 0, ); } return $out; } protected function pickerFiles(array $files) { $out = array(); foreach ($files as $file) { $out[] = array( 'name' => isset($file['name']) ? $file['name'] : '', 'path' => isset($file['path']) ? $file['path'] : '', 'url' => isset($file['url']) ? $file['url'] : '', 'thumb_url' => isset($file['thumb_url']) ? $file['thumb_url'] : '', 'preview_url' => isset($file['preview_url']) ? $file['preview_url'] : '', 'extension' => isset($file['extension']) ? $file['extension'] : '', 'size_label' => isset($file['size_label']) ? $file['size_label'] : '', 'width' => isset($file['width']) ? (int) $file['width'] : 0, 'height' => isset($file['height']) ? (int) $file['height'] : 0, 'webp_url' => !empty($file['has_webp']) && !empty($file['webp_path']) ? $file['webp_path'] : '', ); } return $out; } }