Updated Image Optimizer [NAT] for OpenCart 2.0-2.2 (v1.2.0)
This commit is contained in:
commit
c764f4a26e
|
|
@ -0,0 +1,179 @@
|
||||||
|
=========================================================
|
||||||
|
Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0 — Инструкция / Instructions / Інструкція
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
🇷🇺 РУССКИЙ ЯЗЫК
|
||||||
|
---------------------------------------------------------
|
||||||
|
УСТАНОВКА МОДУЛЯ:
|
||||||
|
1. Распакуйте архив.
|
||||||
|
2. Загрузите содержимое папки upload/ в корень вашего сайта по FTP (системные файлы не перезаписываются).
|
||||||
|
3. Перейдите в админ-панели в "Система" -> "Пользователи" -> "Группы пользователей", выберите вашу группу (Administrator) и установите галочки прав на просмотр и изменение для: extension/module/img_opti
|
||||||
|
4. Перейдите в "Модули / Расширения" -> "Модули", найдите "Image Optimizer [NAT]", нажмите "Активировать" (зеленый плюс), затем "Редактировать".
|
||||||
|
|
||||||
|
ОБНОВЛЕНИЕ:
|
||||||
|
Загрузите новые файлы поверх старых по FTP.
|
||||||
|
|
||||||
|
УДАЛЕНИЕ:
|
||||||
|
Деактивируйте модуль в списке расширений. Удалите файлы модуля:
|
||||||
|
- admin/controller/extension/module/img_opti.php
|
||||||
|
- admin/language/*/extension/module/img_opti.php
|
||||||
|
- admin/view/template/extension/module/img_opti.twig
|
||||||
|
|
||||||
|
КАК РАБОТАТЬ С МОДУЛЕМ:
|
||||||
|
ВНИМАНИЕ! Перед любыми манипуляциями ОБЯЗАТЕЛЬНО сделайте бэкап папки /image/catalog/ на сервере!
|
||||||
|
|
||||||
|
Вкладка 1 (Инфо и статистика):
|
||||||
|
- График диска: Анализирует размер папок (картинки, кэш, корзина). Можно задать лимит тарифа вручную и включить/выключить автообновление графика при входе.
|
||||||
|
- Статистика оптимизации: Отображает общее число обработанных файлов и сэкономленный дисковый объем.
|
||||||
|
|
||||||
|
Вкладка 2 (Сжатие и оптимизация):
|
||||||
|
1. Введите лицензионный токен.
|
||||||
|
2. Выберите папки для сжатия, укажите лимиты по ширине/высоте и качеству (WebP/JPG/PNG).
|
||||||
|
3. Выберите режим (Безопасное копирование в новую папку или Замена оригиналов) и нажмите "СТАРТ ОПТИМИЗАЦИИ".
|
||||||
|
|
||||||
|
Вкладка 3 (Очистка неиспользуемых):
|
||||||
|
1. Выберите папки для поиска мусора.
|
||||||
|
2. Нажмите "НАЙТИ МУСОР". Модуль сверит физические файлы с базой данных и шаблонами.
|
||||||
|
3. Проверьте найденные файлы в логе и отправьте их в "Карантин" (безопасно) или удалите навсегда.
|
||||||
|
|
||||||
|
Вкладка 4 (Водяные знаки):
|
||||||
|
- Настройка правил наложения водяных знаков (изображений или текста) для товаров, категорий, брендов и баннеров "на лету" с фильтрацией по категориям, регулировкой угла наклона, прозрачности и скругления кутов.
|
||||||
|
|
||||||
|
Вкладка 5 (Заглушки):
|
||||||
|
- Автоподстановка картинок-заглушек для товаров, категорий и производителей, у которых изображение не задано в каталоге.
|
||||||
|
|
||||||
|
Вкладка 6 (Форматы изображений 🧪 BETA):
|
||||||
|
- WebP "на лету", поддержка SVG, прогрессивный Lazy Load (эффект плавного размытия blur-up при прокрутке страницы) и переопределение глобальной заглушки для битых ссылок.
|
||||||
|
|
||||||
|
Вкладка 7 (Инструменты (pro) 🧪 BETA):
|
||||||
|
- Санитайзер имен: Очистка имен файлов и папок от кириллицы, пробелов и спецсимволов.
|
||||||
|
- Битые ссылки в БД: Поиск и очистка записей БД с отсутствующими файлами.
|
||||||
|
- Битые файлы на сервере: Поиск и удаление испорченных картинок и файлов по 0 байт.
|
||||||
|
- Поиск тяжелых файлов: Быстрое нахождение самых объемных файлов для ручного сжатия.
|
||||||
|
- Конвертер CMYK: Перекодирование изображений из CMYK в sRGB для корректного показа на iPhone/Safari.
|
||||||
|
|
||||||
|
Вкладка 8 (Крон 🧪 BETA):
|
||||||
|
- Настройка расписания автоматического фонового сжатия папок через системный планировщик задач.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
🇬🇧 ENGLISH
|
||||||
|
---------------------------------------------------------
|
||||||
|
INSTALLATION:
|
||||||
|
1. Extract the archive.
|
||||||
|
2. Upload the contents of the upload/ folder to your OpenCart root directory via FTP (no core files are overwritten).
|
||||||
|
3. Go to System -> Users -> User Groups, select your group (Administrator) and check Access/Modify permissions for: extension/module/img_opti
|
||||||
|
4. Go to Extensions -> Modules, find "Image Optimizer [NAT]", click "Install" (green plus), then "Edit".
|
||||||
|
|
||||||
|
UPDATING:
|
||||||
|
Upload new files over the old ones via FTP.
|
||||||
|
|
||||||
|
UNINSTALLATION:
|
||||||
|
Uninstall the module from the Extensions list. Delete the module files from:
|
||||||
|
- admin/controller/extension/module/img_opti.php
|
||||||
|
- admin/language/*/extension/module/img_opti.php
|
||||||
|
- admin/view/template/extension/module/img_opti.twig
|
||||||
|
|
||||||
|
HOW TO USE (TABS & FEATURES):
|
||||||
|
WARNING! Always create a full backup of your /image/catalog/ folder before running any optimizations or deletions!
|
||||||
|
|
||||||
|
Tab 1 (Info & Stats):
|
||||||
|
- Disk Usage Chart: Renders a pie chart of folder sizes (Image Catalog, Cache, Trash). Supports manual hosting quota limit overrides and auto-update toggling on page open.
|
||||||
|
- Cumulative Stats: Displays total optimized files and total disk space saved.
|
||||||
|
|
||||||
|
Tab 2 (Optimize & Resize):
|
||||||
|
1. Enter your license token.
|
||||||
|
2. Select target folders, adjust Max Width/Height and quality settings (WebP/JPG/PNG).
|
||||||
|
3. Select operation mode (Create a copy or Replace originals) and click "START OPTIMIZATION".
|
||||||
|
|
||||||
|
Tab 3 (Junk Cleaner):
|
||||||
|
1. Select the folders to scan.
|
||||||
|
2. Click "FIND JUNK". The module compares physical files on disk with your Database and Theme files.
|
||||||
|
3. Review the found files in the log and move them to "Quarantine" (safe) or delete them permanently.
|
||||||
|
|
||||||
|
Tab 4 (Watermarks):
|
||||||
|
- Dynamic Overlay rules: Set up watermarks (image or text) to overlay on category, brand, banner, or product images on-the-fly.
|
||||||
|
- Position, transparency, angle, font (TTF), size scaling, and category exclusion/inclusion filters are fully supported.
|
||||||
|
|
||||||
|
Tab 5 (Placeholders):
|
||||||
|
- Renders fallback images for products, categories, or brands that do not have an image assigned in the database, keeping the storefront look consistent.
|
||||||
|
|
||||||
|
Tab 6 (Image Formats 🧪 BETA):
|
||||||
|
- WebP Support: Enable WebP on-the-fly delivery.
|
||||||
|
- SVG Support: Safe SVG rendering.
|
||||||
|
- Progressive Lazy Loading: Blur-up effect (loads micro-preview and fades/de-blurs smoothly as user scrolls).
|
||||||
|
- Fallback Image: Set global placeholder image for broken images.
|
||||||
|
|
||||||
|
Tab 7 (Tools (pro) 🧪 BETA):
|
||||||
|
- Filename Sanitizer: Clear names of cyrillic letters, spaces, and special symbols in file and directory paths.
|
||||||
|
- Broken DB Scanner: Finds and fixes database records referencing non-existent files.
|
||||||
|
- Broken Files Scanner: Scans the server to clean up corrupted or 0-byte images.
|
||||||
|
- Heavy Files: Find the largest images on disk and optimize them instantly.
|
||||||
|
- CMYK Converter: Convert color space of JPEG files from CMYK to sRGB for Apple iOS/Safari compatibility.
|
||||||
|
|
||||||
|
Tab 8 (Cron 🧪 BETA):
|
||||||
|
- Configure automated background directory scans and image compression schedules using a Cron token.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------------------
|
||||||
|
🇺🇦 УКРАЇНСЬКА МОВА
|
||||||
|
---------------------------------------------------------
|
||||||
|
ВСТАНОВЛЕННЯ:
|
||||||
|
1. Розпакуйте архів.
|
||||||
|
2. Завантажте вміст папки upload/ в корінь вашого сайту по FTP (системні файли не перезаписуються).
|
||||||
|
3. Перейдіть у "Система" -> "Користувачі" -> "Групи", виберіть вашу групу (Administrator) і встановіть галочки прав на перегляд і зміну для: extension/module/img_opti
|
||||||
|
4. Перейдіть у "Модулі / Розширення" -> "Модулі", знайдіть "Image Optimizer [NAT]", натисніть "Активувати" (зелений плюс), потім "Редагувати".
|
||||||
|
|
||||||
|
ОНОВЛЕННЯ:
|
||||||
|
Завантажте нові файли поверх старих по FTP.
|
||||||
|
|
||||||
|
ЯК ПРАЦЮВАТИ З МОДУЛЕМ:
|
||||||
|
УВАГА! Перед будь-якими маніпуляціями ОБОВ'ЯЗКОВО зробіть бекап папки /image/catalog/ на сервері!
|
||||||
|
|
||||||
|
Вкладка 1 (Інфо та статистика):
|
||||||
|
- Графік диска: Аналізує розмір папок (зображення, кэш, кошик). Можна задати ліміт тарифу вручну та увімкнути/вимкнути автооновлення графіка при вході.
|
||||||
|
- Статистика оптимізації: Показує загальну кількість оброблених файлів та збережений об`єм диска.
|
||||||
|
|
||||||
|
Вкладка 2 (Стиснення та оптимізація):
|
||||||
|
1. Введіть ліцензійний токен.
|
||||||
|
2. Виберіть папки для стиснення, вкажіть ліміти по ширині/висоті та якості (WebP/JPG/PNG).
|
||||||
|
3. Виберіть режим (Безпечне копіювання в нову папку або Заміна оригіналів) і натисніть "СТАРТ ОПТИМІЗАЦІЇ".
|
||||||
|
|
||||||
|
Вкладка 3 (Очищення невикористовуваних):
|
||||||
|
1. Виберіть папки для пошуку сміття.
|
||||||
|
2. Натисніть "ЗНАЙТИ СМІТТЯ". Модуль звірить фізичні файли з базой даних та шаблонами.
|
||||||
|
3. Перевірте знайдені файли в лозі та відправте їх у "Карантин" (безпечно) або видаліть назавжди.
|
||||||
|
|
||||||
|
Вкладка 4 (Водяні знаки):
|
||||||
|
- Налаштування правил накладання водяних знаків (зображень або тексту) для товарів, категорій, брендів та банерів "на льоту" з фільтрацією за категоріями, регулюванням кута нахилу, прозорості та заокруглення кутів.
|
||||||
|
|
||||||
|
Вкладка 5 (Заглушки):
|
||||||
|
- Автопідстановка картинок-заглушек для товарів, категорій та виробників, у яких зображення не задано в каталозі.
|
||||||
|
|
||||||
|
Вкладка 6 (Формати зображень 🧪 BETA):
|
||||||
|
- WebP "на льоту", підтримка SVG, прогресивний Lazy Load (ефект плавного розмиття blur-up при прокручуванні сторінки) та перевизначення глобальної заглушки для битих посилань.
|
||||||
|
|
||||||
|
Вкладка 7 (Інструменти (pro) 🧪 BETA):
|
||||||
|
- Санітайзер імен: Очищення назв файлів та папок від кирилиці, пробілів та спецсимволів.
|
||||||
|
- Биті посилання в БД: Пошук та очищення записів БД з відсутніми файлами.
|
||||||
|
- Биті файли на сервері: Пошук та вилучення зіпсованих картинок та файлів по 0 байт.
|
||||||
|
- Пошук важких файлів: Швидке знаходження найоб`ємніших файлів для стиснення.
|
||||||
|
- Конвертер CMYK: Перекодування зображень з CMYK в sRGB для коректного відображення на iPhone/Safari.
|
||||||
|
|
||||||
|
Вкладка 8 (Крон 🧪 BETA):
|
||||||
|
- Налаштування розкладу автоматичного фонового стиснення папок через системний планувальник завдань.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=========================================================
|
||||||
|
🔥 OUR OTHER OPENCART EXTENSIONS / НАШИ ДРУГИЕ МОДУЛИ / НАШІ ІНШІ МОДУЛІ:
|
||||||
|
=========================================================
|
||||||
|
• Catalog Optimizer [NAT]: Hide empty categories/brands, clear unused attributes/options.
|
||||||
|
• Domain Scanner [NAT]: Find and clean external links and images.
|
||||||
|
• Promo Top Bar [NAT]: Create interactive promo bars, marquees and header slideshows.
|
||||||
|
|
||||||
|
|
||||||
|
Support: info@nat.od.ua
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,906 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<modification>
|
||||||
|
<name>Image Optimizer [NAT] (OC 2.0-2.2)</name>
|
||||||
|
<id>Image_Optimizer_nat</id>
|
||||||
|
<code>img_opti_nat_od_ua_v1</code>
|
||||||
|
<version>1.2.0</version>
|
||||||
|
<author>NAT</author>
|
||||||
|
<link>mailto:info@nat.od.ua</link>
|
||||||
|
|
||||||
|
<!-- Inject registry global on OC 2.3 -->
|
||||||
|
<file path="system/engine/registry.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[class Registry {]]></search>
|
||||||
|
<add position="after"><![CDATA[
|
||||||
|
public function __construct() {
|
||||||
|
$GLOBALS['registry'] = $this;
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/controller/common/column_left.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if ($this->user->hasPermission('access', 'catalog/product')) {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
$this->load->model('setting/setting');
|
||||||
|
if ($this->config->get('module_img_opti_status')) {
|
||||||
|
$nat_pos = $this->config->get('module_img_opti_menu_position');
|
||||||
|
if ($nat_pos == 'sidebar' || $nat_pos == 'both') {
|
||||||
|
if ($this->user->hasPermission('access', 'module/img_opti')) {
|
||||||
|
$nat_item = array(
|
||||||
|
'name' => 'Image Optimizer',
|
||||||
|
'href' => $this->url->link('module/img_opti', 'token=' . $this->session->data['token'], true),
|
||||||
|
'children' => array()
|
||||||
|
);
|
||||||
|
|
||||||
|
$nat_menu_index = -1;
|
||||||
|
if (isset($data['menus'])) {
|
||||||
|
foreach ($data['menus'] as $k => $menu) {
|
||||||
|
if ($menu['id'] == 'menu-nat') {
|
||||||
|
$nat_menu_index = $k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nat_menu_index > -1) {
|
||||||
|
$data['menus'][$nat_menu_index]['children'][] = $nat_item;
|
||||||
|
} else {
|
||||||
|
$data['menus'][] = array(
|
||||||
|
'id' => 'menu-nat',
|
||||||
|
'icon' => 'fa-rocket fw',
|
||||||
|
'name' => '[NAT] Modules',
|
||||||
|
'href' => '',
|
||||||
|
'children' => array($nat_item)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/controller/common/header.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[$data['logout'] = $this->url->link('common/logout']]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if (!isset($data['nat_menus'])) {
|
||||||
|
$data['nat_menus'] = array();
|
||||||
|
}
|
||||||
|
$this->load->model('setting/setting');
|
||||||
|
if ($this->config->get('module_img_opti_status')) {
|
||||||
|
$nat_pos = $this->config->get('module_img_opti_menu_position');
|
||||||
|
if ($nat_pos == 'header' || $nat_pos == 'both') {
|
||||||
|
if ($this->user->hasPermission('access', 'module/img_opti')) {
|
||||||
|
$data['nat_menus'][] = array(
|
||||||
|
'name' => 'Image Optimizer',
|
||||||
|
'href' => $this->url->link('module/img_opti', 'token=' . $this->session->data['token'], true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/view/template/common/header.tpl">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[<li><a href="<?php echo $logout; ?>"]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
<?php if (isset($nat_menus) && $nat_menus && !isset($nat_menu_rendered)) { ?>
|
||||||
|
<?php $nat_menu_rendered = true; ?>
|
||||||
|
<li class="dropdown" id="nat-ecosystem-menu">
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rocket fa-lg" style="color:#e74c3c;"></i> <span class="hidden-xs hidden-sm hidden-md">[NAT]</span> <span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" id="nat-ecosystem-list">
|
||||||
|
<li class="dropdown-header">Ecosystem [NAT]</li>
|
||||||
|
<?php foreach ($nat_menus as $n_menu) { ?>
|
||||||
|
<li><a href="<?php echo $n_menu['href']; ?>"><i class="fa fa-angle-right"></i> <?php echo $n_menu['name']; ?></a></li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/controller/common/filemanager.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array(utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1)), $allowed)) {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if ($this->config->get('module_img_opti_support_svg') && !in_array('svg', $allowed)) { $allowed[] = 'svg'; }
|
||||||
|
if ($this->config->get('module_img_opti_support_webp') && !in_array('webp', $allowed)) { $allowed[] = 'webp'; }
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array($file['type'], $allowed)) {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if ($this->config->get('module_img_opti_support_svg') && !in_array('image/svg+xml', $allowed)) { $allowed[] = 'image/svg+xml'; }
|
||||||
|
if ($this->config->get('module_img_opti_support_webp')) {
|
||||||
|
if (!in_array('image/webp', $allowed)) { $allowed[] = 'image/webp'; }
|
||||||
|
if (!in_array('image/vnd.webp', $allowed)) { $allowed[] = 'image/vnd.webp'; }
|
||||||
|
if (!in_array('image/x-webp', $allowed)) { $allowed[] = 'image/x-webp'; }
|
||||||
|
$uploaded_ext = utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1));
|
||||||
|
if ($uploaded_ext === 'webp' && !in_array($file['type'], $allowed)) {
|
||||||
|
$allowed[] = $file['type'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[move_uploaded_file($file['tmp_name'], $directory . '/' . $filename);]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if (strtolower(substr(strrchr($filename, '.'), 1)) === 'svg') {
|
||||||
|
$svg_content = @file_get_contents($file['tmp_name']);
|
||||||
|
if ($svg_content) {
|
||||||
|
$svg_content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $svg_content);
|
||||||
|
$svg_content = preg_replace('/on\w+\s*=\s*["\'][^"\']*["\']/i', '', $svg_content);
|
||||||
|
$svg_content = preg_replace('/href\s*=\s*["\']\s*javascript:[^"\']*["\']/i', '', $svg_content);
|
||||||
|
@file_put_contents($file['tmp_name'], $svg_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[$files = glob($directory . '/' . $filter_name . '*.{jpg,jpeg,png,gif,JPG,JPEG,PNG,GIF}', GLOB_BRACE);]]></search>
|
||||||
|
<add position="replace"><![CDATA[
|
||||||
|
$ext_list = array('jpg','jpeg','png','gif','JPG','JPEG','PNG','GIF');
|
||||||
|
if ($this->config->get('module_img_opti_support_svg')) {
|
||||||
|
$ext_list[] = 'svg'; $ext_list[] = 'SVG';
|
||||||
|
}
|
||||||
|
if ($this->config->get('module_img_opti_support_webp')) {
|
||||||
|
$ext_list[] = 'webp'; $ext_list[] = 'WEBP';
|
||||||
|
}
|
||||||
|
$files = glob($directory . '/' . $filter_name . '*.{' . implode(',', $ext_list) . '}', GLOB_BRACE);
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="catalog/model/tool/image.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[public function resize($filename, $width, $height) {]]></search>
|
||||||
|
<add position="after"><![CDATA[
|
||||||
|
$GLOBALS['registry'] = $this->registry;
|
||||||
|
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) {
|
||||||
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback')) {
|
||||||
|
$fallback_image = $this->config->get('module_img_opti_fallback_image');
|
||||||
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
||||||
|
$filename = $fallback_image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($filename) && strpos($filename, '.') !== false) {
|
||||||
|
$nat_ext = strtolower(substr(strrchr($filename, '.'), 1));
|
||||||
|
if ($nat_ext === 'svg' || ($nat_ext === 'webp' && !function_exists('imagewebp'))) {
|
||||||
|
if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on' || ($this->request->server['HTTPS'] == '1')))) {
|
||||||
|
return $this->config->get('config_ssl') . 'image/' . str_replace('\\', '/', $filename);
|
||||||
|
} else {
|
||||||
|
return $this->config->get('config_url') . 'image/' . str_replace('\\', '/', $filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif')))]]></search>
|
||||||
|
<add position="replace"><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'webp')))]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF)))]]></search>
|
||||||
|
<add position="replace"><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF, defined('IMAGETYPE_WEBP') ? IMAGETYPE_WEBP : 18)))]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[$image->save(DIR_IMAGE . $image_new);]]></search>
|
||||||
|
<add position="after"><![CDATA[
|
||||||
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
|
||||||
|
$this->load->model('module/img_opti');
|
||||||
|
try {
|
||||||
|
$this->model_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
|
||||||
|
} catch (\Exception $e) {}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
|
||||||
|
}
|
||||||
|
}]]></search>
|
||||||
|
<add position="replace"><![CDATA[copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
|
||||||
|
}
|
||||||
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
|
||||||
|
$this->load->model('module/img_opti');
|
||||||
|
try {
|
||||||
|
$this->model_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
|
||||||
|
} catch (\Exception $e) {}
|
||||||
|
}
|
||||||
|
}]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/model/tool/image.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[public function resize($filename, $width, $height) {]]></search>
|
||||||
|
<add position="after"><![CDATA[
|
||||||
|
$GLOBALS['registry'] = $this->registry;
|
||||||
|
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) {
|
||||||
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback')) {
|
||||||
|
$fallback_image = $this->config->get('module_img_opti_fallback_image');
|
||||||
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
||||||
|
$filename = $fallback_image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($filename) && strpos($filename, '.') !== false) {
|
||||||
|
$nat_ext = strtolower(substr(strrchr($filename, '.'), 1));
|
||||||
|
if ($nat_ext === 'svg' || ($nat_ext === 'webp' && !function_exists('imagewebp'))) {
|
||||||
|
$nat_https = isset($this->request->server['HTTPS']) && ($this->request->server['HTTPS'] == 'on' || $this->request->server['HTTPS'] == '1');
|
||||||
|
$nat_base = $nat_https ? (defined('HTTPS_CATALOG') ? HTTPS_CATALOG : $this->config->get('config_ssl')) : (defined('HTTP_CATALOG') ? HTTP_CATALOG : $this->config->get('config_url'));
|
||||||
|
return $nat_base . 'image/' . str_replace('\\', '/', $filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif')))]]></search>
|
||||||
|
<add position="replace"><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'webp')))]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF)))]]></search>
|
||||||
|
<add position="replace"><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF, defined('IMAGETYPE_WEBP') ? IMAGETYPE_WEBP : 18)))]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="system/library/image.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if ($this->mime == 'image/gif') {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if (($this->mime == 'image/webp' || $this->mime == 'image/vnd.webp' || $this->mime == 'image/x-webp') && function_exists('imagecreatefromwebp')) {
|
||||||
|
$this->image = @imagecreatefromwebp($file);
|
||||||
|
} else
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if ($extension == 'jpeg' || $extension == 'jpg') {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if ($extension == 'webp' && function_exists('imagewebp')) {
|
||||||
|
imagewebp($this->image, $file, $quality);
|
||||||
|
} else
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[imagejpeg($this->image, $file, $quality);]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
@imageinterlace($this->image, true);
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="system/library/response.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[echo $this->output;]]></search>
|
||||||
|
<add position="replace"><![CDATA[
|
||||||
|
$output = $this->output;
|
||||||
|
$registry = isset($GLOBALS['registry']) ? $GLOBALS['registry'] : null;
|
||||||
|
if ($registry && !defined('DIR_CATALOG')) {
|
||||||
|
$config = $registry->get('config');
|
||||||
|
if ($config && $config->get('module_img_opti_status')) {
|
||||||
|
$webp_on_fly = $config->get('module_img_opti_webp_on_fly');
|
||||||
|
$lazy_load = $config->get('module_img_opti_lazy_load');
|
||||||
|
$broken_fallback = $config->get('module_img_opti_broken_fallback');
|
||||||
|
$fallback_image = $config->get('module_img_opti_fallback_image');
|
||||||
|
|
||||||
|
$theme = $config->get('config_theme');
|
||||||
|
$additional_width = 0;
|
||||||
|
$additional_height = 0;
|
||||||
|
$thumb_width = 0;
|
||||||
|
$thumb_height = 0;
|
||||||
|
|
||||||
|
if ($theme) {
|
||||||
|
$additional_width = $config->get('theme_' . $theme . '_image_additional_width');
|
||||||
|
$additional_height = $config->get('theme_' . $theme . '_image_additional_height');
|
||||||
|
$thumb_width = $config->get('theme_' . $theme . '_image_thumb_width');
|
||||||
|
$thumb_height = $config->get('theme_' . $theme . '_image_thumb_height');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = $config->get('theme_default_image_additional_width'); }
|
||||||
|
if (!$additional_height) { $additional_height = $config->get('theme_default_image_additional_height'); }
|
||||||
|
if (!$thumb_width) { $thumb_width = $config->get('theme_default_image_thumb_width'); }
|
||||||
|
if (!$thumb_height) { $thumb_height = $config->get('theme_default_image_thumb_height'); }
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = $config->get('config_image_additional_width'); }
|
||||||
|
if (!$additional_height) { $additional_height = $config->get('config_image_additional_height'); }
|
||||||
|
if (!$thumb_width) { $thumb_width = $config->get('config_image_thumb_width'); }
|
||||||
|
if (!$thumb_height) { $thumb_height = $config->get('config_image_thumb_height'); }
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = 74; }
|
||||||
|
if (!$additional_height) { $additional_height = 74; }
|
||||||
|
if (!$thumb_width) { $thumb_width = 228; }
|
||||||
|
if (!$thumb_height) { $thumb_height = 228; }
|
||||||
|
|
||||||
|
$svg_style = '<style>' . "\n" .
|
||||||
|
'img[src*=".svg"], img[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'img.mfp-img[src*=".svg"], img.mfp-img[src*=".SVG"], #cboxLoadedContent img[src*=".svg"] {' . "\n" .
|
||||||
|
' width: 800px !important;' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' height: auto !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.thumbnails img[src*=".svg"], .thumbnails img[src*=".SVG"], .thumbnail img[src*=".svg"], .thumbnail img[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' width: ' . $thumb_width . 'px !important;' . "\n" .
|
||||||
|
' height: ' . $thumb_height . 'px !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.image-additional img[src*=".svg"], .image-additional img[src*=".SVG"] {' . "\n" .
|
||||||
|
' width: ' . $additional_width . 'px !important;' . "\n" .
|
||||||
|
' height: ' . $additional_height . 'px !important;' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.product-thumb .image img[src*=".svg"], .product-thumb .image img[src*=".SVG"], .img-responsive[src*=".svg"], .img-responsive[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'</style>';
|
||||||
|
$output = str_ireplace('</head>', $svg_style . '</head>', $output);
|
||||||
|
|
||||||
|
if ($webp_on_fly || $lazy_load || $broken_fallback) {
|
||||||
|
$registry->get('load')->model('tool/image');
|
||||||
|
$model_tool_image = $registry->get('model_tool_image');
|
||||||
|
|
||||||
|
$output = preg_replace_callback('/<img\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly, $lazy_load, $broken_fallback, $fallback_image) {
|
||||||
|
$attrs_str = $matches[1];
|
||||||
|
if (stripos($attrs_str, 'catalog_trash') !== false) {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match_all('/(\b[\w\-:]+)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))/is', $attrs_str, $attr_matches, PREG_SET_ORDER);
|
||||||
|
$attrs = array();
|
||||||
|
foreach ($attr_matches as $am) {
|
||||||
|
$name = strtolower($am[1]);
|
||||||
|
$val = ($am[2] !== '') ? $am[2] : (($am[3] !== '') ? $am[3] : $am[4]);
|
||||||
|
$attrs[$name] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$src = isset($attrs['src']) ? $attrs['src'] : '';
|
||||||
|
$data_src = isset($attrs['data-src']) ? $attrs['data-src'] : '';
|
||||||
|
$has_datasrc = ($data_src !== '');
|
||||||
|
|
||||||
|
$target_url = $has_datasrc ? $data_src : $src;
|
||||||
|
$rel_path = '';
|
||||||
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
||||||
|
$rel_path = urldecode($m_rel[1]);
|
||||||
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
||||||
|
$rel_path = urldecode($m_rel[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$clean_rel_path = preg_replace('/\\?.*/', '', $rel_path);
|
||||||
|
$browser_supports_webp = isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false);
|
||||||
|
|
||||||
|
if ($webp_on_fly && $browser_supports_webp && $clean_rel_path) {
|
||||||
|
$ext = strtolower(substr(strrchr($clean_rel_path, '.'), 1));
|
||||||
|
if (in_array($ext, array('jpg', 'jpeg', 'png'))) {
|
||||||
|
$phys_source = DIR_IMAGE . $clean_rel_path;
|
||||||
|
$phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $clean_rel_path);
|
||||||
|
if (file_exists($phys_source)) {
|
||||||
|
if (!file_exists($phys_webp)) {
|
||||||
|
$img_data = @file_get_contents($phys_source);
|
||||||
|
if ($img_data) {
|
||||||
|
$im = @imagecreatefromstring($img_data);
|
||||||
|
if ($im) {
|
||||||
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
||||||
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
||||||
|
$webp_quality = 80;
|
||||||
|
}
|
||||||
|
@imagewebp($im, $phys_webp, $webp_quality);
|
||||||
|
@imagedestroy($im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_exists($phys_webp)) {
|
||||||
|
if (isset($attrs['src'])) {
|
||||||
|
$attrs['src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['src']);
|
||||||
|
}
|
||||||
|
if (isset($attrs['data-src'])) {
|
||||||
|
$attrs['data-src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['data-src']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($webp_on_fly && $browser_supports_webp) {
|
||||||
|
foreach (array('srcset', 'data-srcset') as $srcset_attr) {
|
||||||
|
if (isset($attrs[$srcset_attr])) {
|
||||||
|
$parts = explode(',', $attrs[$srcset_attr]);
|
||||||
|
foreach ($parts as &$part) {
|
||||||
|
$part = trim($part);
|
||||||
|
$subparts = preg_split('/\\s+/', $part);
|
||||||
|
if (!empty($subparts[0])) {
|
||||||
|
$s_url = $subparts[0];
|
||||||
|
$s_rel = '';
|
||||||
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
||||||
|
$s_rel = urldecode($sm_rel[1]);
|
||||||
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
||||||
|
$s_rel = urldecode($sm_rel[1]);
|
||||||
|
}
|
||||||
|
$s_clean = preg_replace('/\\?.*/', '', $s_rel);
|
||||||
|
if ($s_clean) {
|
||||||
|
$s_ext = strtolower(substr(strrchr($s_clean, '.'), 1));
|
||||||
|
if (in_array($s_ext, array('jpg', 'jpeg', 'png'))) {
|
||||||
|
$s_phys_source = DIR_IMAGE . $s_clean;
|
||||||
|
$s_phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $s_clean);
|
||||||
|
if (file_exists($s_phys_source)) {
|
||||||
|
if (!file_exists($s_phys_webp)) {
|
||||||
|
$img_data = @file_get_contents($s_phys_source);
|
||||||
|
if ($img_data) {
|
||||||
|
$im = @imagecreatefromstring($img_data);
|
||||||
|
if ($im) {
|
||||||
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
||||||
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
||||||
|
$webp_quality = 80;
|
||||||
|
}
|
||||||
|
@imagewebp($im, $s_phys_webp, $webp_quality);
|
||||||
|
@imagedestroy($im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_exists($s_phys_webp)) {
|
||||||
|
$subparts[0] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $s_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$part = implode(' ', $subparts);
|
||||||
|
}
|
||||||
|
$attrs[$srcset_attr] = implode(', ', $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_svg = (strtolower(substr(strrchr($clean_rel_path, '.'), 1)) === 'svg');
|
||||||
|
|
||||||
|
if ($lazy_load && $clean_rel_path && is_file(DIR_IMAGE . $clean_rel_path) && !$is_svg) {
|
||||||
|
try {
|
||||||
|
$tiny_thumb = $model_tool_image->resize($clean_rel_path, 20, 20);
|
||||||
|
if ($has_datasrc) {
|
||||||
|
$attrs['src'] = $tiny_thumb;
|
||||||
|
} else {
|
||||||
|
$attrs['data-src'] = isset($attrs['src']) ? $attrs['src'] : '';
|
||||||
|
$attrs['src'] = $tiny_thumb;
|
||||||
|
}
|
||||||
|
if (isset($attrs['srcset'])) {
|
||||||
|
$attrs['data-srcset'] = $attrs['srcset'];
|
||||||
|
unset($attrs['srcset']);
|
||||||
|
}
|
||||||
|
$class = isset($attrs['class']) ? $attrs['class'] . ' img-lazyload' : 'img-lazyload';
|
||||||
|
$attrs['class'] = $class;
|
||||||
|
|
||||||
|
$style = 'filter: blur(10px); transition: filter 0.3s;';
|
||||||
|
if (isset($attrs['style'])) {
|
||||||
|
$attrs['style'] = rtrim(trim($attrs['style']), ';') . '; ' . $style;
|
||||||
|
} else {
|
||||||
|
$attrs['style'] = $style;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($broken_fallback) {
|
||||||
|
$fallback_url = '';
|
||||||
|
$req_obj = $registry->get('request');
|
||||||
|
$is_https = isset($req_obj->server['HTTPS']) && ($req_obj->server['HTTPS'] == 'on' || $req_obj->server['HTTPS'] == '1');
|
||||||
|
$server_url = $is_https ? $config->get('config_ssl') : $config->get('config_url');
|
||||||
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
||||||
|
$fallback_url = $server_url . 'image/' . $fallback_image;
|
||||||
|
} elseif ($config->get('config_logo') && is_file(DIR_IMAGE . $config->get('config_logo'))) {
|
||||||
|
$fallback_url = $server_url . 'image/' . $config->get('config_logo');
|
||||||
|
} else {
|
||||||
|
$fallback_url = $server_url . 'image/no_image.png';
|
||||||
|
}
|
||||||
|
if ($fallback_url) {
|
||||||
|
$attrs['onerror'] = "this.src='" . $fallback_url . "'; this.srcset=''; this.onerror=null;";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_attrs = array();
|
||||||
|
foreach ($attrs as $k => $v) {
|
||||||
|
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
|
||||||
|
}
|
||||||
|
return '<img ' . implode(' ', $new_attrs) . '>';
|
||||||
|
}, $output);
|
||||||
|
|
||||||
|
if ($lazy_load) {
|
||||||
|
$lazy_script = '<style>img.img-lazyload-loaded { filter: blur(0) !important; }</style>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
var lazyImages = [].slice.call(document.querySelectorAll("img.img-lazyload"));
|
||||||
|
if ("IntersectionObserver" in window) {
|
||||||
|
var lazyImageObserver = new IntersectionObserver(function(entries, observer) {
|
||||||
|
entries.forEach(function(entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
var lazyImage = entry.target;
|
||||||
|
if (lazyImage.dataset.src) {
|
||||||
|
lazyImage.src = lazyImage.dataset.src;
|
||||||
|
}
|
||||||
|
if (lazyImage.dataset.srcset) {
|
||||||
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
||||||
|
}
|
||||||
|
lazyImage.addEventListener("load", function() {
|
||||||
|
lazyImage.classList.remove("img-lazyload");
|
||||||
|
lazyImage.classList.add("img-lazyload-loaded");
|
||||||
|
});
|
||||||
|
lazyImageObserver.unobserve(lazyImage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
lazyImages.forEach(function(lazyImage) {
|
||||||
|
lazyImageObserver.observe(lazyImage);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
lazyImages.forEach(function(lazyImage) {
|
||||||
|
if (lazyImage.dataset.src) {
|
||||||
|
lazyImage.src = lazyImage.dataset.src;
|
||||||
|
}
|
||||||
|
if (lazyImage.dataset.srcset) {
|
||||||
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
||||||
|
}
|
||||||
|
lazyImage.style.filter = "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>';
|
||||||
|
$output = str_ireplace('</body>', $lazy_script, $output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->output = $output;
|
||||||
|
echo $this->output;
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[echo $output;]]></search>
|
||||||
|
<add position="replace"><![CDATA[
|
||||||
|
$registry = isset($GLOBALS['registry']) ? $GLOBALS['registry'] : null;
|
||||||
|
if ($registry && !defined('DIR_CATALOG')) {
|
||||||
|
$config = $registry->get('config');
|
||||||
|
if ($config && $config->get('module_img_opti_status')) {
|
||||||
|
$webp_on_fly = $config->get('module_img_opti_webp_on_fly');
|
||||||
|
$lazy_load = $config->get('module_img_opti_lazy_load');
|
||||||
|
$broken_fallback = $config->get('module_img_opti_broken_fallback');
|
||||||
|
$fallback_image = $config->get('module_img_opti_fallback_image');
|
||||||
|
|
||||||
|
$theme = $config->get('config_theme');
|
||||||
|
$additional_width = 0;
|
||||||
|
$additional_height = 0;
|
||||||
|
$thumb_width = 0;
|
||||||
|
$thumb_height = 0;
|
||||||
|
|
||||||
|
if ($theme) {
|
||||||
|
$additional_width = $config->get('theme_' . $theme . '_image_additional_width');
|
||||||
|
$additional_height = $config->get('theme_' . $theme . '_image_additional_height');
|
||||||
|
$thumb_width = $config->get('theme_' . $theme . '_image_thumb_width');
|
||||||
|
$thumb_height = $config->get('theme_' . $theme . '_image_thumb_height');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = $config->get('theme_default_image_additional_width'); }
|
||||||
|
if (!$additional_height) { $additional_height = $config->get('theme_default_image_additional_height'); }
|
||||||
|
if (!$thumb_width) { $thumb_width = $config->get('theme_default_image_thumb_width'); }
|
||||||
|
if (!$thumb_height) { $thumb_height = $config->get('theme_default_image_thumb_height'); }
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = $config->get('config_image_additional_width'); }
|
||||||
|
if (!$additional_height) { $additional_height = $config->get('config_image_additional_height'); }
|
||||||
|
if (!$thumb_width) { $thumb_width = $config->get('config_image_thumb_width'); }
|
||||||
|
if (!$thumb_height) { $thumb_height = $config->get('config_image_thumb_height'); }
|
||||||
|
|
||||||
|
if (!$additional_width) { $additional_width = 74; }
|
||||||
|
if (!$additional_height) { $additional_height = 74; }
|
||||||
|
if (!$thumb_width) { $thumb_width = 228; }
|
||||||
|
if (!$thumb_height) { $thumb_height = 228; }
|
||||||
|
|
||||||
|
$svg_style = '<style>' . "\n" .
|
||||||
|
'img[src*=".svg"], img[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'img.mfp-img[src*=".svg"], img.mfp-img[src*=".SVG"], #cboxLoadedContent img[src*=".svg"] {' . "\n" .
|
||||||
|
' width: 800px !important;' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' height: auto !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.thumbnails img[src*=".svg"], .thumbnails img[src*=".SVG"], .thumbnail img[src*=".svg"], .thumbnail img[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' width: ' . $thumb_width . 'px !important;' . "\n" .
|
||||||
|
' height: ' . $thumb_height . 'px !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.image-additional img[src*=".svg"], .image-additional img[src*=".SVG"] {' . "\n" .
|
||||||
|
' width: ' . $additional_width . 'px !important;' . "\n" .
|
||||||
|
' height: ' . $additional_height . 'px !important;' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'.product-thumb .image img[src*=".svg"], .product-thumb .image img[src*=".SVG"], .img-responsive[src*=".svg"], .img-responsive[src*=".SVG"] {' . "\n" .
|
||||||
|
' max-width: 100% !important;' . "\n" .
|
||||||
|
' max-height: 100% !important;' . "\n" .
|
||||||
|
' object-fit: contain !important;' . "\n" .
|
||||||
|
'}' . "\n" .
|
||||||
|
'</style>';
|
||||||
|
$output = str_ireplace('</head>', $svg_style . '</head>', $output);
|
||||||
|
|
||||||
|
if ($webp_on_fly || $lazy_load || $broken_fallback) {
|
||||||
|
$registry->get('load')->model('tool/image');
|
||||||
|
$model_tool_image = $registry->get('model_tool_image');
|
||||||
|
|
||||||
|
$output = preg_replace_callback('/<img\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly, $lazy_load, $broken_fallback, $fallback_image) {
|
||||||
|
$attrs_str = $matches[1];
|
||||||
|
if (stripos($attrs_str, 'catalog_trash') !== false) {
|
||||||
|
return $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match_all('/(\b[\w\-:]+)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))/is', $attrs_str, $attr_matches, PREG_SET_ORDER);
|
||||||
|
$attrs = array();
|
||||||
|
foreach ($attr_matches as $am) {
|
||||||
|
$name = strtolower($am[1]);
|
||||||
|
$val = ($am[2] !== '') ? $am[2] : (($am[3] !== '') ? $am[3] : $am[4]);
|
||||||
|
$attrs[$name] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$src = isset($attrs['src']) ? $attrs['src'] : '';
|
||||||
|
$data_src = isset($attrs['data-src']) ? $attrs['data-src'] : '';
|
||||||
|
$has_datasrc = ($data_src !== '');
|
||||||
|
|
||||||
|
$target_url = $has_datasrc ? $data_src : $src;
|
||||||
|
$rel_path = '';
|
||||||
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
||||||
|
$rel_path = urldecode($m_rel[1]);
|
||||||
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
||||||
|
$rel_path = urldecode($m_rel[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$clean_rel_path = preg_replace('/\\?.*/', '', $rel_path);
|
||||||
|
$browser_supports_webp = isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false);
|
||||||
|
|
||||||
|
if ($webp_on_fly && $browser_supports_webp && $clean_rel_path) {
|
||||||
|
$ext = strtolower(substr(strrchr($clean_rel_path, '.'), 1));
|
||||||
|
if (in_array($ext, array('jpg', 'jpeg', 'png'))) {
|
||||||
|
$phys_source = DIR_IMAGE . $clean_rel_path;
|
||||||
|
$phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $clean_rel_path);
|
||||||
|
if (file_exists($phys_source)) {
|
||||||
|
if (!file_exists($phys_webp)) {
|
||||||
|
$img_data = @file_get_contents($phys_source);
|
||||||
|
if ($img_data) {
|
||||||
|
$im = @imagecreatefromstring($img_data);
|
||||||
|
if ($im) {
|
||||||
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
||||||
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
||||||
|
$webp_quality = 80;
|
||||||
|
}
|
||||||
|
@imagewebp($im, $phys_webp, $webp_quality);
|
||||||
|
@imagedestroy($im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_exists($phys_webp)) {
|
||||||
|
if (isset($attrs['src'])) {
|
||||||
|
$attrs['src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['src']);
|
||||||
|
}
|
||||||
|
if (isset($attrs['data-src'])) {
|
||||||
|
$attrs['data-src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['data-src']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($webp_on_fly && $browser_supports_webp) {
|
||||||
|
foreach (array('srcset', 'data-srcset') as $srcset_attr) {
|
||||||
|
if (isset($attrs[$srcset_attr])) {
|
||||||
|
$parts = explode(',', $attrs[$srcset_attr]);
|
||||||
|
foreach ($parts as &$part) {
|
||||||
|
$part = trim($part);
|
||||||
|
$subparts = preg_split('/\\s+/', $part);
|
||||||
|
if (!empty($subparts[0])) {
|
||||||
|
$s_url = $subparts[0];
|
||||||
|
$s_rel = '';
|
||||||
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
||||||
|
$s_rel = urldecode($sm_rel[1]);
|
||||||
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
||||||
|
$s_rel = urldecode($sm_rel[1]);
|
||||||
|
}
|
||||||
|
$s_clean = preg_replace('/\\?.*/', '', $s_rel);
|
||||||
|
if ($s_clean) {
|
||||||
|
$s_ext = strtolower(substr(strrchr($s_clean, '.'), 1));
|
||||||
|
if (in_array($s_ext, array('jpg', 'jpeg', 'png'))) {
|
||||||
|
$s_phys_source = DIR_IMAGE . $s_clean;
|
||||||
|
$s_phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $s_clean);
|
||||||
|
if (file_exists($s_phys_source)) {
|
||||||
|
if (!file_exists($s_phys_webp)) {
|
||||||
|
$img_data = @file_get_contents($s_phys_source);
|
||||||
|
if ($img_data) {
|
||||||
|
$im = @imagecreatefromstring($img_data);
|
||||||
|
if ($im) {
|
||||||
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
||||||
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
||||||
|
$webp_quality = 80;
|
||||||
|
}
|
||||||
|
@imagewebp($im, $s_phys_webp, $webp_quality);
|
||||||
|
@imagedestroy($im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_exists($s_phys_webp)) {
|
||||||
|
$subparts[0] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $s_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$part = implode(' ', $subparts);
|
||||||
|
}
|
||||||
|
$attrs[$srcset_attr] = implode(', ', $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_svg = (strtolower(substr(strrchr($clean_rel_path, '.'), 1)) === 'svg');
|
||||||
|
|
||||||
|
if ($lazy_load && $clean_rel_path && is_file(DIR_IMAGE . $clean_rel_path) && !$is_svg) {
|
||||||
|
try {
|
||||||
|
$tiny_thumb = $model_tool_image->resize($clean_rel_path, 20, 20);
|
||||||
|
if ($has_datasrc) {
|
||||||
|
$attrs['src'] = $tiny_thumb;
|
||||||
|
} else {
|
||||||
|
$attrs['data-src'] = isset($attrs['src']) ? $attrs['src'] : '';
|
||||||
|
$attrs['src'] = $tiny_thumb;
|
||||||
|
}
|
||||||
|
if (isset($attrs['srcset'])) {
|
||||||
|
$attrs['data-srcset'] = $attrs['srcset'];
|
||||||
|
unset($attrs['srcset']);
|
||||||
|
}
|
||||||
|
$class = isset($attrs['class']) ? $attrs['class'] . ' img-lazyload' : 'img-lazyload';
|
||||||
|
$attrs['class'] = $class;
|
||||||
|
|
||||||
|
$style = 'filter: blur(10px); transition: filter 0.3s;';
|
||||||
|
if (isset($attrs['style'])) {
|
||||||
|
$attrs['style'] = rtrim(trim($attrs['style']), ';') . '; ' . $style;
|
||||||
|
} else {
|
||||||
|
$attrs['style'] = $style;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($broken_fallback) {
|
||||||
|
$fallback_url = '';
|
||||||
|
$req_obj = $registry->get('request');
|
||||||
|
$is_https = isset($req_obj->server['HTTPS']) && ($req_obj->server['HTTPS'] == 'on' || $req_obj->server['HTTPS'] == '1');
|
||||||
|
$server_url = $is_https ? $config->get('config_ssl') : $config->get('config_url');
|
||||||
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
||||||
|
$fallback_url = $server_url . 'image/' . $fallback_image;
|
||||||
|
} elseif ($config->get('config_logo') && is_file(DIR_IMAGE . $config->get('config_logo'))) {
|
||||||
|
$fallback_url = $server_url . 'image/' . $config->get('config_logo');
|
||||||
|
} else {
|
||||||
|
$fallback_url = $server_url . 'image/no_image.png';
|
||||||
|
}
|
||||||
|
if ($fallback_url) {
|
||||||
|
$attrs['onerror'] = "this.src='" . $fallback_url . "'; this.srcset=''; this.onerror=null;";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_attrs = array();
|
||||||
|
foreach ($attrs as $k => $v) {
|
||||||
|
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
|
||||||
|
}
|
||||||
|
return '<img ' . implode(' ', $new_attrs) . '>';
|
||||||
|
}, $output);
|
||||||
|
|
||||||
|
if ($lazy_load) {
|
||||||
|
$lazy_script = '<style>img.img-lazyload-loaded { filter: blur(0) !important; }</style>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
var lazyImages = [].slice.call(document.querySelectorAll("img.img-lazyload"));
|
||||||
|
if ("IntersectionObserver" in window) {
|
||||||
|
var lazyImageObserver = new IntersectionObserver(function(entries, observer) {
|
||||||
|
entries.forEach(function(entry) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
var lazyImage = entry.target;
|
||||||
|
if (lazyImage.dataset.src) {
|
||||||
|
lazyImage.src = lazyImage.dataset.src;
|
||||||
|
}
|
||||||
|
if (lazyImage.dataset.srcset) {
|
||||||
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
||||||
|
}
|
||||||
|
lazyImage.addEventListener("load", function() {
|
||||||
|
lazyImage.classList.remove("img-lazyload");
|
||||||
|
lazyImage.classList.add("img-lazyload-loaded");
|
||||||
|
});
|
||||||
|
lazyImageObserver.unobserve(lazyImage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
lazyImages.forEach(function(lazyImage) {
|
||||||
|
lazyImageObserver.observe(lazyImage);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
lazyImages.forEach(function(lazyImage) {
|
||||||
|
if (lazyImage.dataset.src) {
|
||||||
|
lazyImage.src = lazyImage.dataset.src;
|
||||||
|
}
|
||||||
|
if (lazyImage.dataset.srcset) {
|
||||||
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
||||||
|
}
|
||||||
|
lazyImage.style.filter = "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>';
|
||||||
|
$output = str_ireplace('</body>', $lazy_script, $output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo $output;
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="catalog/controller/product/product.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[if ($product_info['image']) {]]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
if (!$product_info['image'] && $this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback') && $this->config->get('module_img_opti_fallback_image')) {
|
||||||
|
$product_info['image'] = $this->config->get('module_img_opti_fallback_image');
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<!-- Inject sidebar menu item on OC 2.0/2.1 (menu.php / menu.tpl) -->
|
||||||
|
<file path="admin/controller/common/menu.php">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[$data['module'] = $this->url->link('extension/module']]></search>
|
||||||
|
<add position="before"><![CDATA[
|
||||||
|
$this->load->model('setting/setting');
|
||||||
|
if ($this->config->get('module_img_opti_status')) {
|
||||||
|
$nat_pos = $this->config->get('module_img_opti_menu_position');
|
||||||
|
if ($nat_pos == 'sidebar' || $nat_pos == 'both') {
|
||||||
|
if ($this->user->hasPermission('access', 'module/img_opti')) {
|
||||||
|
$data['module_img_opti'] = $this->url->link('module/img_opti', 'token=' . $this->session->data['token'], 'SSL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="admin/view/template/common/menu.tpl">
|
||||||
|
<operation error="skip">
|
||||||
|
<search><![CDATA[<li><a href="<?php echo $module; ?>"><?php echo $text_module; ?></a></li>]]></search>
|
||||||
|
<add position="after"><![CDATA[
|
||||||
|
<?php if (isset($module_img_opti)) { ?>
|
||||||
|
<li><a href="<?php echo $module_img_opti; ?>">Image Optimizer</a></li>
|
||||||
|
<?php } ?>
|
||||||
|
]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<file path="system/library/sitecreator/trueFileManager/ControllerCommonTrueFileManager.php|admin/controller/common/truefilemanager.php" error="skip">
|
||||||
|
<operation>
|
||||||
|
<search><![CDATA['image/pjpeg', 'image/x-png'),]]></search>
|
||||||
|
<add position="replace"><![CDATA['image/pjpeg', 'image/x-png', ($this->config->get('module_img_opti_truefilemanager_webp') || $this->config->get('img_opti_truefilemanager_webp')) ? 'image/webp' : ''),]]></add>
|
||||||
|
</operation>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
</modification>
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
================================================================================
|
||||||
|
Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
Thank you for purchasing our extension!
|
||||||
|
If you have any questions, feel free to contact us: info@nat.od.ua
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
TERMS & CONDITIONS (License & Support Policy)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
By purchasing and downloading this extension, you strictly agree to the following terms:
|
||||||
|
|
||||||
|
1. Single Domain License: One purchase allows you to install and use the extension on exactly one (1) live domain and one (1) local/test environment. You need a separate License Key for each additional live project.
|
||||||
|
2. License Activation: The module requires a License Key to function. Please email info@nat.od.ua with your Order ID and Domain Name after purchase to receive your key.
|
||||||
|
3. Support: We provide free technical support for bugs and standard installation issues. Support is available via email (info@nat.od.ua). Please allow up to 24-48 hours for a response.
|
||||||
|
4. Compatibility: The module is guaranteed to work on standard OpenCart / ocStore installations. We cannot guarantee out-of-the-box compatibility with heavily customized core files or third-party checkout/theme modules, though we will do our best to assist you.
|
||||||
|
5. Refund Policy: We offer a 100% refund if the extension is completely non-functional on a standard OpenCart setup and our support team is unable to fix it. We do not issue refunds for "change of mind" or if you have already requested and received a domain license key.
|
||||||
|
6. Distribution: The code is open-source (no IonCube), meaning you can modify it for your own needs. However, you may not resell, redistribute, share, or publish this extension in any form.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
INSTALLATION
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
1. Extract the archive.
|
||||||
|
2. Upload the contents of the `upload/` folder to your OpenCart root directory via FTP (no core files will be overwritten).
|
||||||
|
3. Go to OpenCart Admin -> System -> Users -> User Groups. Select your group (Administrator) and check both "Access" and "Modify" permissions for `extension/module/img_opti`.
|
||||||
|
4. Go to Extensions -> Extensions -> Modules. Find "Image Optimizer [NAT]" and click the "Install" (green plus) button.
|
||||||
|
5. Click the "Edit" button to access the module panel.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
HOW TO USE (TABS & FEATURES)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
WARNING: Always create a full backup of your `/image/catalog/` folder before running any optimizations or deletions!
|
||||||
|
|
||||||
|
TAB 1: INFO & STATS
|
||||||
|
- Disk Usage Chart: Renders a pie chart of folder sizes (Image Catalog, Cache, Trash). Supports manual hosting quota limit overrides and auto-update toggling on page open.
|
||||||
|
- Cumulative Stats: Displays total optimized files and total disk space saved.
|
||||||
|
|
||||||
|
TAB 2: OPTIMIZE & RESIZE (COMPRESSION)
|
||||||
|
1. Enter your License Key (Token) in the respective field.
|
||||||
|
2. Select the target folders inside `image/catalog/` you want to process.
|
||||||
|
3. Choose the Mode: "Create a copy" (Safest) or "Replace originals".
|
||||||
|
4. Set your maximum Width/Height and WebP/JPG/PNG quality settings.
|
||||||
|
5. Click "START OPTIMIZATION". The module will process files in batches using AJAX without overloading your server.
|
||||||
|
|
||||||
|
TAB 3: UNUSED IMAGES CLEANER (JUNK REMOVAL)
|
||||||
|
1. Select the folders to scan.
|
||||||
|
2. Click "FIND JUNK". The module compares physical files on disk with your Database and Theme files.
|
||||||
|
3. Review the detailed log of unused files.
|
||||||
|
4. Click "To Quarantine" to safely move the junk files to `image/catalog_trash/`, or "Delete permanently" to wipe them from the server.
|
||||||
|
5. You can easily restore files from Quarantine with one click if something goes wrong.
|
||||||
|
|
||||||
|
TAB 4: WATERMARKS
|
||||||
|
- Dynamic Overlay rules: Set up watermarks (image or text) to overlay on category, brand, banner, or product images on-the-fly.
|
||||||
|
- Position, transparency, angle, font (TTF), size scaling, and category exclusion/inclusion filters are fully supported.
|
||||||
|
|
||||||
|
TAB 5: PLACEHOLDERS
|
||||||
|
- Renders fallback images for products, categories, or brands that do not have an image assigned in the database, keeping the storefront look consistent.
|
||||||
|
|
||||||
|
TAB 6: IMAGE FORMATS (🧪 BETA)
|
||||||
|
- WebP Support: Enable WebP on-the-fly delivery.
|
||||||
|
- SVG Support: Safe SVG rendering.
|
||||||
|
- Progressive Lazy Loading: Blur-up effect (loads micro-preview and fades/de-blurs smoothly as user scrolls).
|
||||||
|
- Fallback Image: Set global placeholder image for broken images.
|
||||||
|
|
||||||
|
TAB 7: TOOLS (PRO) (🧪 BETA)
|
||||||
|
- Filename Sanitizer: Clear names of cyrillic letters, spaces, and special symbols in file and directory paths.
|
||||||
|
- Broken DB Scanner: Finds and fixes database records referencing non-existent files.
|
||||||
|
- Broken Files Scanner: Scans the server to clean up corrupted or 0-byte images.
|
||||||
|
- Heavy Files: Find the largest images on disk and optimize them instantly.
|
||||||
|
- CMYK Converter: Convert color space of JPEG files from CMYK to sRGB for Apple iOS/Safari compatibility.
|
||||||
|
|
||||||
|
TAB 8: CRON (🧪 BETA)
|
||||||
|
- Configure automated background directory scans and image compression schedules using a Cron token.
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
UNINSTALLATION
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
1. Uninstall the module from the Extensions list in your OpenCart Admin.
|
||||||
|
2. Delete the module files from:
|
||||||
|
- admin/controller/extension/module/img_opti.php
|
||||||
|
- admin/language/*/extension/module/img_opti.php
|
||||||
|
- admin/view/template/extension/module/img_opti.twig
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
🔥 CHECK OUT OUR OTHER OPENCART MODULES:
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
Maximize your store's performance with our professional toolset:
|
||||||
|
|
||||||
|
• Catalog Optimizer [NAT]: Hide empty categories/brands, clear unused attributes/options.
|
||||||
|
• Domain Scanner [NAT]: Find and clean external links and images.
|
||||||
|
• Promo Top Bar [NAT]: Create interactive promo bars, marquees and header slideshows.
|
||||||
|
|
||||||
|
👉 View all our extensions here:
|
||||||
|
https://www.opencart.com/index.php?route=marketplace/extension&filter_member=nat.odua
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
Support info@nat.od.ua | All rights reserved.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,565 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Compress Originals & Delete Junk';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Compress Originals & Delete Junk';
|
||||||
|
$_['text_extension'] = 'Extensions';
|
||||||
|
$_['text_edit'] = 'Module Settings';
|
||||||
|
$_['text_success'] = 'Settings have been successfully saved!';
|
||||||
|
$_['entry_warning'] = 'WARNING: Always make a backup of your files, especially the /image/catalog folder before starting!';
|
||||||
|
$_['text_tech'] = 'Server Technology Check:';
|
||||||
|
$_['text_author'] = 'Technical support and suggestions: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Optimization & Resize';
|
||||||
|
$_['tab_cleaner'] = 'Clean Unused Images';
|
||||||
|
$_['tab_info'] = 'Info / Ecosystem [NAT]';
|
||||||
|
$_['tab_settings'] = 'Settings';
|
||||||
|
$_['tab_tools'] = 'Tools (pro)';
|
||||||
|
$_['tab_cron'] = 'Cron Jobs';
|
||||||
|
$_['entry_mode'] = 'Operation Mode';
|
||||||
|
$_['text_copy'] = 'Create a copy (Safe)';
|
||||||
|
$_['text_replace'] = 'Replace originals (Requires backup!)';
|
||||||
|
$_['entry_folder'] = 'Folder name for copy';
|
||||||
|
$_['entry_batch'] = 'Files per batch';
|
||||||
|
$_['entry_log_limit'] = 'Log lines per page';
|
||||||
|
$_['entry_max_width'] = 'Max width (px) [0 - no limit]';
|
||||||
|
$_['entry_max_height'] = 'Max height (px) [0 - no limit]';
|
||||||
|
$_['entry_jpg'] = 'JPG Quality (0-100)';
|
||||||
|
$_['entry_png'] = 'PNG Compression (0-9)';
|
||||||
|
$_['entry_webp'] = 'WebP Quality (0-100)';
|
||||||
|
$_['entry_targets'] = 'What to optimize?';
|
||||||
|
$_['text_all_catalog'] = '[ Entire image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Files only in image/catalog root';
|
||||||
|
$_['entry_threshold'] = 'Replacement threshold (%)';
|
||||||
|
$_['help_threshold'] = '0: replace only if file is smaller. 10: allow up to 10% file size increase.';
|
||||||
|
$_['entry_token'] = 'Activation Token (Root Domain)';
|
||||||
|
$_['entry_status'] = 'Module Status';
|
||||||
|
$_['entry_menu_position'] = 'Menu Position [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Modules List Only';
|
||||||
|
$_['text_menu_header'] = 'Top Header';
|
||||||
|
$_['text_menu_sidebar'] = 'Sidebar Menu';
|
||||||
|
$_['text_menu_both'] = 'Both (Header + Sidebar)';
|
||||||
|
$_['text_status_on'] = 'Enabled';
|
||||||
|
$_['text_status_off'] = 'Disabled';
|
||||||
|
$_['entry_engine'] = 'Image Processing Engine';
|
||||||
|
$_['text_engine_gd'] = 'GD (Standard)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (High quality, sRGB profile keeping)';
|
||||||
|
$_['entry_license'] = 'License';
|
||||||
|
$_['text_cleaner_info'] = 'This tool looks for files that physically exist on the server but are not linked in the database or template files.';
|
||||||
|
$_['entry_clean_targets'] = 'Folders to scan for junk?';
|
||||||
|
$_['entry_extended_log'] = 'Extended log (show used photos and their locations)';
|
||||||
|
$_['entry_process_broken'] = 'Include files with broken encoding ("?" in names)';
|
||||||
|
$_['button_scan_trash'] = 'FIND JUNK FILES';
|
||||||
|
$_['button_quarantine'] = 'Move to Quarantine (Safe)';
|
||||||
|
$_['button_delete_trash'] = 'Delete Permanently';
|
||||||
|
$_['button_empty_quarantine'] = 'Empty Quarantine Folder';
|
||||||
|
$_['button_restore_quarantine'] = 'Restore from Quarantine';
|
||||||
|
$_['text_scan_result'] = 'Scan Results';
|
||||||
|
$_['text_trash_files'] = 'Junk files found: ';
|
||||||
|
$_['text_trash_size'] = 'Potential space to free up: ';
|
||||||
|
$_['button_start'] = 'START OPTIMIZATION';
|
||||||
|
$_['button_pause'] = 'PAUSE';
|
||||||
|
$_['button_stop'] = 'STOP';
|
||||||
|
$_['button_continue'] = 'CONTINUE';
|
||||||
|
$_['button_start_new'] = 'START (New Run)';
|
||||||
|
$_['button_download_log'] = 'Download TXT Log';
|
||||||
|
$_['button_download'] = 'Download';
|
||||||
|
$_['button_save'] = 'Save';
|
||||||
|
$_['button_cancel'] = 'Cancel';
|
||||||
|
$_['text_progress'] = 'Progress:';
|
||||||
|
$_['text_old_size'] = 'Before:';
|
||||||
|
$_['text_new_size'] = 'After:';
|
||||||
|
$_['text_savings'] = 'Saved:';
|
||||||
|
$_['text_detailed_log'] = 'Detailed Log';
|
||||||
|
$_['text_page'] = 'Page';
|
||||||
|
$_['text_waiting'] = 'Waiting to start...';
|
||||||
|
$_['text_enabled'] = 'Enabled';
|
||||||
|
$_['text_disabled_error'] = 'Disabled (ERROR)';
|
||||||
|
$_['text_supported'] = 'Supported';
|
||||||
|
$_['text_no'] = 'No';
|
||||||
|
$_['error_permission'] = 'You do not have permission to modify this module!';
|
||||||
|
$_['error_license'] = 'Access Error: Invalid activation token for this domain!';
|
||||||
|
$_['error_domain'] = ' (Domain: ';
|
||||||
|
$_['text_found'] = 'Files found: ';
|
||||||
|
$_['text_compressed'] = 'Compressed';
|
||||||
|
$_['text_original'] = 'Original';
|
||||||
|
$_['text_error_process'] = 'Processing error: ';
|
||||||
|
$_['error_log_empty'] = 'Log is empty! Run the process first.';
|
||||||
|
$_['text_report_header'] = '=== REPORT ===';
|
||||||
|
$_['text_total_files'] = 'Total files: ';
|
||||||
|
$_['text_searching'] = 'Searching for files...';
|
||||||
|
$_['error_ajax'] = 'Error: ';
|
||||||
|
$_['text_resumed'] = '▶ PROCESS RESUMED...';
|
||||||
|
$_['text_paused'] = '⏸ PAUSED. Process suspended.';
|
||||||
|
$_['confirm_stop'] = 'Are you sure you want to completely abort the process?';
|
||||||
|
$_['confirm_delete'] = 'WARNING! Files will be permanently deleted from the server with no chance of recovery! Continue?';
|
||||||
|
$_['confirm_empty_q'] = 'Are you sure you want to completely empty the quarantine folder?';
|
||||||
|
$_['confirm_restore'] = 'All files from quarantine will be returned to their original locations. Continue?';
|
||||||
|
$_['button_stopping'] = 'Stopping...';
|
||||||
|
$_['text_stopped_user'] = '🛑 PROCESS ABORTED BY USER!';
|
||||||
|
$_['text_increase'] = 'Increased';
|
||||||
|
$_['text_done'] = '✅ DONE! Process completed.';
|
||||||
|
$_['error_timeout'] = 'Server timeout. Auto-retrying in 3 seconds...';
|
||||||
|
$_['text_building_index'] = 'Step 1/2: Indexing DB and templates (searching for used photos)...';
|
||||||
|
$_['text_comparing'] = 'Step 2/2: Comparing physical files with the index...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Quarantined to image/catalog_trash/ successfully!';
|
||||||
|
$_['text_deleted_done'] = '✅ Junk deletion completed successfully!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Quarantine folder emptied successfully!';
|
||||||
|
$_['text_restore_done'] = '✅ Files restored from quarantine to their original locations!';
|
||||||
|
$_['text_quarantine_move'] = 'Quarantined: ';
|
||||||
|
$_['text_deleted_file'] = 'Deleted: ';
|
||||||
|
$_['text_no_trash'] = 'No junk found in selected folders! All files are in use.';
|
||||||
|
$_['text_scanning_dirs'] = 'Scanning directories: ';
|
||||||
|
$_['text_log_used'] = '[IN USE]';
|
||||||
|
$_['text_log_trash'] = '[JUNK]';
|
||||||
|
$_['text_log_deleted'] = '[DELETED]';
|
||||||
|
$_['text_log_del_err'] = '[DELETE ERROR]';
|
||||||
|
$_['text_log_quarantine'] = '[QUARANTINED]';
|
||||||
|
$_['text_log_q_err'] = '[MOVE ERROR]';
|
||||||
|
$_['text_log_restored'] = '[RESTORED]';
|
||||||
|
$_['text_log_rest_err'] = '[RESTORE ERROR]';
|
||||||
|
$_['text_used_in'] = 'in: ';
|
||||||
|
$_['text_analyzing'] = 'Analyzing database...';
|
||||||
|
$_['text_clean_empty'] = 'Clean! No junk found.';
|
||||||
|
$_['text_processing'] = 'Processing...';
|
||||||
|
$_['text_restoring'] = 'Restoring files...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (And all subfolders)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Root only)';
|
||||||
|
$_['text_q_empty_err'] = 'Quarantine folder is empty or does not exist.';
|
||||||
|
$_['error_token'] = 'Invalid token. Please refresh the page.';
|
||||||
|
$_['text_apply_title'] = 'Optimization completed in folder: ';
|
||||||
|
$_['text_apply_info'] = 'Check the result. If everything looks good, apply changes to the main catalog.';
|
||||||
|
$_['entry_apply_backup'] = 'Move and create a backup (in Quarantine)';
|
||||||
|
$_['entry_apply_replace'] = 'Replace originals permanently';
|
||||||
|
$_['button_apply_main'] = 'APPLY TO MAIN CATALOG';
|
||||||
|
$_['button_delete_copies'] = 'Delete temporary copies (Cancel)';
|
||||||
|
$_['button_full_cleanup'] = 'Full cleanup of all temp folders and backups';
|
||||||
|
$_['text_apply_success'] = '✅ Files successfully moved. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Originals saved in folder: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Originals deleted.';
|
||||||
|
$_['text_error_source'] = 'Error: Folder with optimized files not found.';
|
||||||
|
$_['confirm_full_cleanup'] = 'WARNING! This action will permanently delete ALL backup folders (backup_*) and temp copy folders. Continue?';
|
||||||
|
$_['text_tools_desc'] = 'Global scanners and tools for file system and database image optimization.';
|
||||||
|
$_['text_tool_select'] = 'Select Tool:';
|
||||||
|
$_['text_group_scanners'] = 'Scanners & Analysis';
|
||||||
|
$_['text_group_generators'] = 'Generators & Processing';
|
||||||
|
$_['text_group_experimental'] = 'Experimental Features (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Warning: Experimental features modify system files or the database. Backup is highly recommended!';
|
||||||
|
$_['text_tool_broken_db'] = 'Broken/Empty DB Image Scanner';
|
||||||
|
$_['text_tool_watermark'] = 'Dynamic Watermark (Cache only)';
|
||||||
|
$_['text_tool_duplicates'] = 'Duplicate Finder (MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Heavy PNG to JPG Converter (non-transparent PNG scanner)';
|
||||||
|
$_['text_tool_placeholders'] = 'Placeholder Generator (Replaces No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Clean EXIF Data (Geotags/Meta)';
|
||||||
|
$_['text_tool_small_photos'] = 'Too Small Photo Scanner';
|
||||||
|
$_['text_tool_html_broken'] = 'Broken Images in HTML';
|
||||||
|
$_['text_tool_empty_folders'] = 'Remove Empty Folders (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Smart Cache Cleanup';
|
||||||
|
$_['text_tool_folder_tree'] = 'Folder Tree Statistics';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (No DB)';
|
||||||
|
$_['text_tool_translit'] = 'File Name Transliteration';
|
||||||
|
$_['text_tool_cache_restore'] = 'Restore Originals from Cache';
|
||||||
|
$_['entry_wm_type'] = 'Watermark Type';
|
||||||
|
$_['text_wm_image'] = 'Image (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Text';
|
||||||
|
$_['entry_wm_image_path'] = 'Image Path';
|
||||||
|
$_['entry_wm_text_val'] = 'Watermark Text';
|
||||||
|
$_['entry_wm_position'] = 'Position (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Opacity (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Only for Categories';
|
||||||
|
$_['entry_wm_brand'] = 'Only for Brands';
|
||||||
|
$_['entry_min_width'] = 'Minimum Width (px)';
|
||||||
|
$_['entry_min_height'] = 'Minimum Height (px)';
|
||||||
|
$_['entry_ph_product'] = 'Placeholder for Products';
|
||||||
|
$_['entry_ph_category'] = 'Placeholder for Categories';
|
||||||
|
$_['entry_ph_brand'] = 'Placeholder for Brands';
|
||||||
|
$_['button_run_tool'] = 'Run Tool';
|
||||||
|
$_['button_scan_tool'] = 'Scan';
|
||||||
|
$_['button_fix_tool'] = 'Fix Found';
|
||||||
|
$_['text_th_path'] = 'File / Path';
|
||||||
|
$_['text_th_size'] = 'Size';
|
||||||
|
$_['text_th_action'] = 'Action';
|
||||||
|
$_['text_th_status'] = 'Status';
|
||||||
|
$_['text_th_problem'] = 'Problem / Value';
|
||||||
|
$_['text_cron_title'] = 'Cron Settings';
|
||||||
|
$_['text_cron_desc'] = 'Add the command to your hosting Cron for automatic background compression.';
|
||||||
|
$_['entry_cron_new_only'] = 'Process only new (unprocessed) files';
|
||||||
|
$_['entry_cron_folders'] = 'Folders to scan (leave empty for entire catalog)';
|
||||||
|
$_['text_promo_title'] = 'Other [NAT] Series Modules';
|
||||||
|
$_['text_promo_desc'] = '[NAT] Series Modules are tools for deep OpenCart optimization. We focus on automation, DB speed, and server cleanup. All modules are open-source and share the same core logic.';
|
||||||
|
$_['text_support'] = 'Technical support and suggestions:';
|
||||||
|
$_['button_more'] = 'View all extensions on OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Hide empty categories/brands + SEO sorting (in stock first) + DB junk cleanup.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Sort by exact date';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Automated SEO page for new arrivals. Displays items strictly by the real date added. Sliders, grid, and smart date archive sidebar.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Current Module.</strong> Compress and resize ORIGINAL photos (image/catalog) to WebP + Server cleanup of unused images.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Find and clean external links and HTTP images in DB and theme files. Ideal for HTTPS migration or cleanup after parsing.';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Create polls and voting widgets with a visual design builder, deep statistics, and cheat protection.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Intelligent garbage cleanup in the database, system log management, and crucial index addition for speed boost.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Import/export prices and stock levels from/to files (xls, xlsx, csv, xml, json) or by url. Field mapping, Cron automation, text utilities.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Handy administrator tools: login as customer, admin customization, user permissions management, and hiding unnecessary menu items.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Automatic tracking of UTM tags during checkout. Saves the traffic source, campaign, keywords, and displays this data in order details to analyze advertising effectiveness.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Upper promotional info-banner, information bar above header, marquee and top-banner';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Create interactive promo bars and header slideshows with a block builder, countdown timers, subscription forms, and flexible targeting.';
|
||||||
|
$_['text_tool_no_image'] = 'Products without main image (empty in DB)';
|
||||||
|
$_['text_problem_main_broken'] = 'Broken main image (file deleted)';
|
||||||
|
$_['text_problem_add_broken'] = 'Broken additional image (file deleted)';
|
||||||
|
$_['text_problem_no_image'] = 'Missing main image';
|
||||||
|
$_['text_err_dir'] = 'Error: Directory not found or invalid path.';
|
||||||
|
$_['text_err_no_queue'] = 'Error: No queue found. Run scan first.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Deleted empty folder: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'No empty folders found in image/catalog/.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Total empty folders removed: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Image cache has been cleared.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Watermarks are operating in SMART mode: originals remain untouched, applied only on cache generation.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Scanning image/catalog/ for MD5 duplicates... This may take a while.';
|
||||||
|
$_['text_log_found_dupe'] = 'Found duplicate: ';
|
||||||
|
$_['text_log_no_dupes'] = 'No duplicates found.';
|
||||||
|
$_['text_log_total_dupes'] = 'Found duplicates: %s. Potential savings: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Click "Fix Found" to redirect DB links to original and delete duplicates.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Fixed and removed duplicate: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Successfully fixed %s duplicates. DB updated.';
|
||||||
|
$_['text_log_scan_png'] = 'Scanning for heavy PNG files without transparency...';
|
||||||
|
$_['text_log_no_png'] = 'No suitable PNG files found.';
|
||||||
|
$_['text_log_total_png'] = 'Found %s PNG without transparency. Click "Fix Found" to convert to JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Converted to JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Successfully converted %s files. Space saved: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Updated %s Products with placeholder.';
|
||||||
|
$_['text_log_ph_cat'] = 'Updated %s Categories with placeholder.';
|
||||||
|
$_['text_log_ph_brand'] = 'Updated %s Brands with placeholder.';
|
||||||
|
$_['text_log_ph_empty'] = 'No placeholders selected in settings.';
|
||||||
|
$_['text_log_scan_exif'] = 'Scanning for EXIF data...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Stripped EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'No EXIF data found to clean.';
|
||||||
|
$_['text_log_success_exif'] = 'Cleaned EXIF from %s files. Saved: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Scanning for photos smaller than %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Too small (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'No small photos found.';
|
||||||
|
$_['text_log_total_small'] = 'Found %s small photos. Please review and re-upload.';
|
||||||
|
$_['text_log_html_broken'] = 'Broken IMG in HTML table %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'No broken images found in HTML descriptions.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Found %s broken links in HTML. Please edit them manually.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Scanning image/cache/ for orphaned files...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Deleted orphaned cache: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'No orphaned cache files found.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Removed %s orphaned cache files. Space saved: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Scanning for files with cyrillic or spaces...';
|
||||||
|
$_['text_log_no_bad_names'] = 'No files with bad names found.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Found %s bad files. Click "Fix Found" to transliterate and update DB.';
|
||||||
|
$_['text_log_renamed'] = 'Renamed: ';
|
||||||
|
$_['text_log_success_translit'] = 'Successfully transliterated %s files and updated DB.';
|
||||||
|
$_['text_log_scan_restore'] = 'Scanning image/cache/ to restore lost originals...';
|
||||||
|
$_['text_log_restored_cache'] = 'Restored from cache: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'No missing originals found in cache.';
|
||||||
|
$_['text_log_success_restore'] = 'Successfully restored %s original images from cache.';
|
||||||
|
$_['error_invalid_folder'] = 'Error: Invalid folder name!';
|
||||||
|
$_['text_formats_title'] = 'Modern Formats (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Settings for integrating modern formats into the OpenCart file system and templates.';
|
||||||
|
$_['entry_support_svg'] = 'SVG Upload Support';
|
||||||
|
$_['help_support_svg'] = 'Allows uploading vector SVG images via the standard Filemanager and safely displaying them on the storefront without size distortion.';
|
||||||
|
$_['entry_support_webp'] = 'WebP Upload Support';
|
||||||
|
$_['help_support_webp'] = 'Allows manually uploading, linking, and displaying ready .webp files on the website via the Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP in TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Adds WebP upload, preview, and thumbnail support to sitecreator\'s TrueFileManager (if installed).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Ultra-fast replacement: automatically changes .jpg/.png extensions to .webp directly in the storefront HTML output and adds the loading="lazy" attribute. Does not touch the database!';
|
||||||
|
$_['help_tool_broken_db'] = 'Finds images in the database whose physical files have been deleted from the server. Clears broken links.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Recursively scans image/catalog/ and safely removes all folders that contain no files.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Builds a directory tree and shows the real size of each folder to find space eaters.';
|
||||||
|
$_['help_tool_html_broken'] = 'Scans HTML descriptions of products and categories for <img> tags with non-existent images.';
|
||||||
|
$_['help_tool_small_photos'] = 'Finds pictures with resolution below the specified one. Added search links to Google and Yandex Images so you can easily find high-quality replacements.';
|
||||||
|
$_['help_tool_duplicates'] = 'Searches for exact duplicates by MD5 hash (different names, same image). Merges them into one file and updates the DB.';
|
||||||
|
$_['help_tool_watermark'] = 'Applies watermarks in SMART mode (only to storefront cache, originals remain unchanged).';
|
||||||
|
$_['help_tool_placeholders'] = 'Massively sets the selected placeholder image for products and categories that have no photo.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Scans for heavy PNG files without transparency (alpha channel). Since they do not need transparency, they can be safely converted to lightweight JPG. This greatly reduces file size (typically by 70-80%) and speeds up your website, automatically updating all links in the database.';
|
||||||
|
$_['help_tool_exif'] = 'Scans JPEG files and removes hidden EXIF data (geotags, camera data), reducing size by 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Finds cached files in image/cache/ whose originals have already been deleted, and removes this trash.';
|
||||||
|
$_['help_tool_translit'] = 'Finds files with Cyrillic or spaces, renames them using transliteration, and updates paths in the DB.';
|
||||||
|
$_['help_tool_cache_restore'] = 'If you accidentally deleted an original from catalog, this tool will try to find its copy in cache and restore it.';
|
||||||
|
$_['entry_quarantine'] = 'Move originals to Quarantine (instead of deleting)';
|
||||||
|
$_['entry_backup_db'] = 'Create database backup of modified tables in SQL before starting';
|
||||||
|
$_['button_fix_selected'] = 'FIX SELECTED';
|
||||||
|
$_['button_export_log'] = 'Export to CSV';
|
||||||
|
$_['text_th_before'] = 'State BEFORE';
|
||||||
|
$_['text_th_after'] = 'Expected result AFTER';
|
||||||
|
$_['text_th_select'] = 'Action';
|
||||||
|
$_['text_th_preview'] = 'Preview';
|
||||||
|
$_['text_th_name'] = 'Object / File';
|
||||||
|
$_['text_global_action'] = 'Bulk action for selected:';
|
||||||
|
$_['text_action_clear'] = 'Clear DB Link';
|
||||||
|
$_['text_action_disable'] = 'Disable (Status = 0)';
|
||||||
|
$_['text_action_stock'] = 'Set Out of Stock';
|
||||||
|
$_['text_action_placeholder'] = 'Set Placeholder';
|
||||||
|
$_['text_action_ignore'] = 'Do nothing';
|
||||||
|
$_['text_action_merge'] = 'Merge (delete duplicate, keep original)';
|
||||||
|
$_['text_action_convert'] = 'Convert to JPG';
|
||||||
|
$_['text_action_rename'] = 'Rename to translit';
|
||||||
|
$_['text_action_delete'] = 'Delete';
|
||||||
|
$_['text_action_restore'] = 'Restore';
|
||||||
|
$_['text_db_backup_created'] = 'Database backup created: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Quarantined to: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Successfully executed';
|
||||||
|
$_['text_fix_status_error'] = 'Error: %s';
|
||||||
|
$_['text_log_exported'] = 'Log successfully exported!';
|
||||||
|
$_['text_select_action'] = '-- Select Action --';
|
||||||
|
$_['error_empty_selection'] = 'Error: No rows selected for fixing!';
|
||||||
|
$_['text_link_admin'] = 'Admin';
|
||||||
|
$_['text_link_catalog'] = 'Store';
|
||||||
|
$_['entry_threads'] = 'Processing Threads';
|
||||||
|
$_['text_threads_optimal'] = 'optimal';
|
||||||
|
$_['button_download_zip'] = 'Download ZIP and Delete Folder';
|
||||||
|
$_['confirm_download_and_delete'] = 'Did the ZIP download successfully? Can we delete the temporary copies folder on the server?';
|
||||||
|
$_['button_clear_backups'] = 'Delete DB Backups';
|
||||||
|
$_['button_clear_backups_short'] = 'Clear Backups';
|
||||||
|
$_['confirm_clear_backups'] = 'Are you sure you want to delete all SQL database backups?';
|
||||||
|
$_['text_compare_title'] = 'Compare Images BEFORE / AFTER';
|
||||||
|
$_['text_compare_before'] = 'BEFORE';
|
||||||
|
$_['text_compare_after'] = 'AFTER';
|
||||||
|
$_['text_backups_cleared'] = 'All database backups deleted successfully!';
|
||||||
|
$_['error_no_backups'] = 'No backups found or folder is empty.';
|
||||||
|
$_['button_restore'] = 'Restore';
|
||||||
|
$_['button_empty'] = 'Clear';
|
||||||
|
$_['entry_wm_targets'] = 'Apply watermark to:';
|
||||||
|
$_['text_wm_target_product'] = 'Product Photos';
|
||||||
|
$_['text_wm_target_category'] = 'Category Photos';
|
||||||
|
$_['text_wm_target_brand'] = 'Brand Logos';
|
||||||
|
$_['text_wm_target_banner'] = 'Banners and Slides';
|
||||||
|
$_['text_wm_target_blog'] = 'Articles / Blog';
|
||||||
|
$_['error_no_scan_data'] = 'Error: No scan data found. Please run scan first!';
|
||||||
|
$_['error_row_not_found'] = 'Error: Row not found in scan results!';
|
||||||
|
$_['error_file_not_found'] = 'Error: File not found on server!';
|
||||||
|
$_['text_folder_rules_title'] = 'Individual Folder Rules';
|
||||||
|
$_['text_folder_rules_desc'] = 'You can override compression quality and maximum sizes for specific folders. Leave a field blank to use global settings. The path is relative to the image/ directory (e.g. catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Folder Path';
|
||||||
|
$_['entry_jpg_quality'] = 'JPG Quality';
|
||||||
|
$_['entry_webp_quality'] = 'WebP Quality';
|
||||||
|
$_['button_add_rule'] = 'Add Rule';
|
||||||
|
$_['button_remove'] = 'Remove';
|
||||||
|
$_['entry_wm_angle'] = 'Watermark Rotation Angle (degrees)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Watermark Image Corner Radius (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Watermark Scaling';
|
||||||
|
$_['entry_wm_size_percent'] = 'Watermark Size (% of image width)';
|
||||||
|
$_['entry_wm_text_color'] = 'Watermark Text Color';
|
||||||
|
$_['entry_wm_text_font'] = 'Watermark Font (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Categories/Brands Filter Mode';
|
||||||
|
$_['text_wm_filter_include'] = 'Only for Selected';
|
||||||
|
$_['text_wm_filter_exclude'] = 'For All Except Selected';
|
||||||
|
$_['text_wm_size_original'] = 'Original Size';
|
||||||
|
$_['text_wm_size_percent'] = 'Proportional to image width (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Preview Watermark';
|
||||||
|
$_['text_wm_preview_title'] = 'Watermark Preview';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Exclude folders from scan';
|
||||||
|
$_['text_cron_entities'] = 'Which photo types to process?';
|
||||||
|
$_['text_cron_quality_override'] = 'Override compression settings for Cron';
|
||||||
|
$_['text_cron_recommendations'] = 'Recommended Cron Schedule';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>For small sites (up to 5000 products)</b>: run Cron every hour (<code>0 * * * *</code>).</li><li><b>For medium sites (5000-20000 products)</b>: run every 3-6 hours (<code>0 */6 * * *</code>).</li><li><b>For large sites (over 20000 products)</b>: run once a day at night (e.g. at 2:00 AM: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Server Broken/Empty Files Scanner';
|
||||||
|
$_['help_tool_broken_files'] = 'Scans physical files in image/catalog/ for corrupted images or empty files of 0 bytes.';
|
||||||
|
$_['text_grid_broken_file'] = 'Broken or empty file on server';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Delete or replace with placeholder';
|
||||||
|
$_['text_description_broken_files'] = 'Scanning results of physical files in image/catalog/. Shows broken (corrupted) images or files with 0 bytes size.';
|
||||||
|
$_['text_action_strip'] = 'Strip EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Set Placeholder';
|
||||||
|
$_['text_th_entity'] = 'Entity / Related Links';
|
||||||
|
$_['text_cron_entity_product'] = 'Products';
|
||||||
|
$_['text_cron_entity_category'] = 'Categories';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Manufacturers / Brands';
|
||||||
|
$_['text_cron_entity_banner'] = 'Banners';
|
||||||
|
$_['text_cron_entity_blog'] = 'Blog / Articles';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'JPG Quality (Cron)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'WebP Quality (Cron)';
|
||||||
|
$_['entry_cron_max_width'] = 'Max Width px (Cron)';
|
||||||
|
$_['entry_cron_max_height'] = 'Max Height px (Cron)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Folders to scan';
|
||||||
|
$_['text_no_fonts'] = 'No TTF fonts found in system/library/font/';
|
||||||
|
$_['button_apply'] = 'Apply';
|
||||||
|
$_['text_backup_will_be_created'] = 'A database backup will be automatically created before running this action.';
|
||||||
|
$_['text_bulk_action'] = 'Bulk action for selected';
|
||||||
|
$_['text_grid_results'] = 'Scanner Results & Details';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Original files will be moved to quarantine (image/catalog_trash/) before modification.';
|
||||||
|
$_['text_success_apply'] = 'Changes successfully applied!';
|
||||||
|
$_['text_description_broken'] = 'Database scan results for broken links (table records referencing non-existent files).';
|
||||||
|
$_['text_description_cache_restore'] = 'Results of search for cache files whose originals are missing in the catalog.';
|
||||||
|
$_['text_description_duplicates'] = 'Results of search for exact duplicate images by MD5 hash.';
|
||||||
|
$_['text_description_empty_folders'] = 'List of empty folders in image/catalog/ directory for deletion.';
|
||||||
|
$_['text_description_exif'] = 'Results of search for images containing EXIF metadata that can be stripped.';
|
||||||
|
$_['text_description_folder_tree'] = 'Folder size statistics in image/catalog/ directory.';
|
||||||
|
$_['text_description_html_broken'] = 'Results of search for non-existent image files referenced in HTML descriptions.';
|
||||||
|
$_['text_description_placeholders'] = 'Results of search for entities without images to fill with placeholders.';
|
||||||
|
$_['text_description_png_jpg'] = 'Results of search for heavy PNG images without transparency that can be compressed to JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'List of images with dimensions smaller than the minimum specified (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Results of search for cache files whose original images have already been deleted.';
|
||||||
|
$_['text_description_translit'] = 'Results of search for files with Cyrillic characters or spaces in their names.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Clear link or set placeholder';
|
||||||
|
$_['text_grid_dupe_after'] = 'Original (remains)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Duplicate (will be deleted)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Empty folder';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Delete empty folder';
|
||||||
|
$_['text_grid_exif_after'] = 'Strip metadata';
|
||||||
|
$_['text_grid_exif_before'] = 'Contains EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Fix link manually';
|
||||||
|
$_['text_grid_missing_file'] = 'File is missing';
|
||||||
|
$_['text_grid_missing_html'] = 'Image not found in description';
|
||||||
|
$_['text_grid_missing_orig'] = 'Original is missing';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Restore original from cache';
|
||||||
|
$_['text_grid_no_image'] = 'No image (empty)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Orphaned cache';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Delete unneeded cache';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Set brand placeholder';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Set category placeholder';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Set product placeholder';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Recommended to replace with larger one';
|
||||||
|
$_['text_grid_too_small'] = 'Too small';
|
||||||
|
$_['text_no_relations'] = 'No database relations';
|
||||||
|
$_['text_action_archive'] = 'Archive (to ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Archive + Delete';
|
||||||
|
$_['text_tool_sanitizer'] = 'File Name Sanitizer';
|
||||||
|
$_['help_tool_sanitizer'] = 'Bulk cleanup of file and folder names from Cyrillic, spaces and special characters, normalization of extensions.';
|
||||||
|
$_['text_description_sanitizer'] = 'Results of file name analysis for sanitization based on selected rules.';
|
||||||
|
$_['entry_sz_translit'] = 'Transliterate (Cyrillic to Latin)';
|
||||||
|
$_['entry_sz_spaces'] = 'Replace spaces with "_"';
|
||||||
|
$_['entry_sz_special'] = 'Remove special characters';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Lowercase file name';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Lowercase extension';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Normalize extension (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Folder filtering mode';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Scan all folders except selected (Exceptions)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Scan only selected folders';
|
||||||
|
$_['button_select_all'] = 'Select All';
|
||||||
|
$_['button_deselect_all'] = 'Deselect All';
|
||||||
|
$_['entry_wm_upload_font'] = 'Upload custom TTF font';
|
||||||
|
$_['button_upload_font'] = 'Upload Font';
|
||||||
|
$_['text_success_font_upload'] = 'Font successfully uploaded!';
|
||||||
|
$_['error_font_upload'] = 'Error uploading font! Only .ttf files up to 5 MB are allowed.';
|
||||||
|
$_['entry_lazy_load'] = 'Progressive Lazy Load';
|
||||||
|
$_['help_lazy_load'] = 'Enables a premium progressive "blur-up" lazy loading on the frontend. Images are replaced with tiny blurred versions and loaded fully when scrolled into view.';
|
||||||
|
$_['confirm_replace_mode'] = 'WARNING! You have selected the "Replace originals" mode. All images will be overwritten directly on the server. We strongly recommend making a backup first. Are you sure you want to continue?';
|
||||||
|
$_['text_compare'] = 'Compare';
|
||||||
|
$_['text_home'] = 'Home';
|
||||||
|
$_['text_tech_gd'] = 'GD Library';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'WebP Support';
|
||||||
|
$_['text_position_1'] = '1 - Top Left';
|
||||||
|
$_['text_position_2'] = '2 - Top Center';
|
||||||
|
$_['text_position_3'] = '3 - Top Right';
|
||||||
|
$_['text_position_4'] = '4 - Middle Left';
|
||||||
|
$_['text_position_5'] = '5 - Center';
|
||||||
|
$_['text_position_6'] = '6 - Middle Right';
|
||||||
|
$_['text_position_7'] = '7 - Bottom Left';
|
||||||
|
$_['text_position_8'] = '8 - Bottom Center';
|
||||||
|
$_['text_position_9'] = '9 - Bottom Right';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Log Level';
|
||||||
|
$_['text_log_changed_only'] = 'Changed files only';
|
||||||
|
$_['text_log_all'] = 'All files (verbose)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Skipped (no improvement)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Reset optimization cache (force full rescan)';
|
||||||
|
$_['confirm_reset_cache'] = 'Reset optimization cache? Next scan will check ALL files again.';
|
||||||
|
$_['text_cache_cleared'] = 'Optimization cache cleared. Next scan will check all files.';
|
||||||
|
$_['error_wm_preview'] = 'Failed to generate watermark preview.';
|
||||||
|
$_['error_wm_too_large'] = 'Watermark image is too large (%dx%d px)! Maximum allowed size is %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'WARNING: Some files could not be replaced (locked or permission denied):';
|
||||||
|
$_['text_action_compress'] = 'Compress Images';
|
||||||
|
$_['text_estimated'] = 'Estimated';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Min savings (bytes)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Min savings (%)';
|
||||||
|
$_['text_optimized'] = 'Optimized';
|
||||||
|
$_['entry_broken_fallback'] = 'Broken Image Fallback';
|
||||||
|
$_['help_broken_fallback'] = 'Automatically replaces missing or corrupted catalog images on the storefront with a fallback image.';
|
||||||
|
$_['entry_fallback_image'] = 'Fallback Image';
|
||||||
|
$_['help_fallback_image'] = 'Select a custom image to display when a product or category image is broken. Defaults to no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'CMYK to RGB Converter';
|
||||||
|
$_['help_tool_cmyk'] = 'Convert JPEG images from CMYK color profile to sRGB to ensure correct display on Safari and iOS devices.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Top Heavy Files Scanner';
|
||||||
|
$_['help_tool_heavy_files'] = 'Scan and find the heaviest original files in catalog directory and optimize them directly.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Max files to find (total limit)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Convert to RGB';
|
||||||
|
$_['text_description_cmyk'] = 'CMYK JPEGs found. Conversion to sRGB guarantees they are displayed correctly on all platforms, including iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'List of the top %d heaviest files in the image directory. You can optimize them here directly.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Status: All folders will be scanned (no exclusions).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Status: All folders will be scanned EXCEPT the %d checked folders.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Status: Only the %d checked folders will be scanned.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'WARNING: No folders are selected! The Cron job will scan nothing.';
|
||||||
|
$_['text_action_apply_changes'] = 'Apply specified changes';
|
||||||
|
$_['entry_wm_status'] = 'Watermark Status';
|
||||||
|
$_['text_disabled'] = 'Disabled';
|
||||||
|
$_['entry_rule_name'] = 'Rule Name';
|
||||||
|
$_['entry_rule_status'] = 'Rule Status';
|
||||||
|
$_['entry_action'] = 'Action';
|
||||||
|
$_['entry_ph_status'] = 'Placeholder Status';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Max files to scan (limit)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Min Size Threshold';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Max files to scan (limit)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Min Size Threshold';
|
||||||
|
$_['text_wm_rule_title'] = 'Watermark Rule';
|
||||||
|
$_['button_close'] = 'Close';
|
||||||
|
$_['text_ph_rule_title'] = 'Placeholder Rule';
|
||||||
|
$_['entry_ph_image'] = 'Placeholder Image';
|
||||||
|
$_['button_clear_cache_wm'] = 'Clear Cache (apply watermarks)';
|
||||||
|
$_['text_active'] = 'Active';
|
||||||
|
$_['text_error'] = 'Not Active';
|
||||||
|
$_['text_th_dupe_delete'] = 'File to delete (Duplicate)';
|
||||||
|
$_['text_th_dupe_keep'] = 'File to keep (Original)';
|
||||||
|
$_['text_dupe_original_label'] = 'Original';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Duplicate';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Disk Usage';
|
||||||
|
$_['text_cumulative_stats'] = 'Optimization Statistics';
|
||||||
|
$_['text_disk_catalog'] = 'Catalog images';
|
||||||
|
$_['text_disk_cache'] = 'OC Cache';
|
||||||
|
$_['text_disk_trash'] = 'Module trash';
|
||||||
|
$_['text_disk_other'] = 'Other';
|
||||||
|
$_['text_disk_free'] = 'Free';
|
||||||
|
$_['text_total_files_opt'] = 'Files optimized';
|
||||||
|
$_['text_total_saved'] = 'Total saved';
|
||||||
|
$_['text_avg_saving_pct'] = 'Avg. savings';
|
||||||
|
$_['text_stats_hint'] = 'Statistics accumulate across all optimization sessions (stored locally)';
|
||||||
|
$_['text_reset_stats'] = 'Reset statistics';
|
||||||
|
$_['text_reset_stats_confirm']= 'Are you sure you want to reset optimization statistics?';
|
||||||
|
$_['text_export_import'] = 'Export / Import Settings';
|
||||||
|
$_['button_export_settings'] = 'Export settings (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Import settings';
|
||||||
|
$_['help_export_settings'] = 'Download all module settings as a JSON file';
|
||||||
|
$_['help_import_settings'] = 'Upload a previously exported JSON to restore settings';
|
||||||
|
$_['text_import_success'] = 'Settings imported successfully. Reload the page to apply.';
|
||||||
|
$_['error_import_file'] = 'No file uploaded or upload error';
|
||||||
|
$_['error_import_invalid'] = 'Invalid settings file (wrong module or format)';
|
||||||
|
$_['text_click_to_load'] = 'Click ↑ to load disk usage';
|
||||||
|
$_['tab_formats'] = 'Image Formats';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Hosting Disk Quota Limits (manual)';
|
||||||
|
$_['text_disk_quota_desc'] = 'If the disk space graph shows incorrect data (the server\'s physical partition instead of your hosting account limit), you can specify your hosting quota limits manually. Enter 0 for auto-detect.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Total Hosting Disk Space (GB)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)';
|
||||||
|
$_['entry_auto_disk'] = 'Auto-update on page load';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Task Name';
|
||||||
|
$_['entry_cron_task_summary'] = 'Compression settings';
|
||||||
|
$_['button_add_cron_task'] = 'Add Cron Task';
|
||||||
|
$_['button_copy_cron_url'] = 'Copy URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'Cron task execution URL:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'License Key Required';
|
||||||
|
$_['text_license_required_desc'] = 'Please enter your license key to activate the module. All configuration and optimization features are locked until a valid key is provided.';
|
||||||
|
$_['entry_token_desc'] = 'Enter the activation token issued for your root domain. You can obtain it from your purchase page or by contacting support.';
|
||||||
|
$_['button_activate'] = 'Activate Module';
|
||||||
|
$_['text_support'] = 'Technical Support';
|
||||||
|
$_['text_support_desc'] = 'If you don\'t have a key yet, or face issues, please provide your order ID and domain name.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Watermark Max Width (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Watermark Max Height (px)';
|
||||||
|
$_['help_wm_max_width'] = 'If the source watermark image exceeds this width, it will be automatically scaled down. Set 0 or leave empty for default 800px.';
|
||||||
|
$_['help_wm_max_height'] = 'If the source watermark image exceeds this height, it will be automatically scaled down. Set 0 or leave empty for default 800px.';
|
||||||
|
$_['entry_disk_status'] = 'Enable Disk Space Scan & Graph';
|
||||||
|
$_['help_disk_status'] = 'If enabled, the module will periodically scan the image folder size and display a pie chart. Disable this on large sites to improve settings page load speed.';
|
||||||
|
$_['text_disk_settings_title'] = 'Disk Limits & Scanning Settings';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,565 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Compress Originals & Delete Junk';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Compress Originals & Delete Junk';
|
||||||
|
$_['text_extension'] = 'Extensions';
|
||||||
|
$_['text_edit'] = 'Module Settings';
|
||||||
|
$_['text_success'] = 'Settings have been successfully saved!';
|
||||||
|
$_['entry_warning'] = 'WARNING: Always make a backup of your files, especially the /image/catalog folder before starting!';
|
||||||
|
$_['text_tech'] = 'Server Technology Check:';
|
||||||
|
$_['text_author'] = 'Technical support and suggestions: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Optimization & Resize';
|
||||||
|
$_['tab_cleaner'] = 'Clean Unused Images';
|
||||||
|
$_['tab_info'] = 'Info / Ecosystem [NAT]';
|
||||||
|
$_['tab_settings'] = 'Settings';
|
||||||
|
$_['tab_tools'] = 'Tools (pro)';
|
||||||
|
$_['tab_cron'] = 'Cron Jobs';
|
||||||
|
$_['entry_mode'] = 'Operation Mode';
|
||||||
|
$_['text_copy'] = 'Create a copy (Safe)';
|
||||||
|
$_['text_replace'] = 'Replace originals (Requires backup!)';
|
||||||
|
$_['entry_folder'] = 'Folder name for copy';
|
||||||
|
$_['entry_batch'] = 'Files per batch';
|
||||||
|
$_['entry_log_limit'] = 'Log lines per page';
|
||||||
|
$_['entry_max_width'] = 'Max width (px) [0 - no limit]';
|
||||||
|
$_['entry_max_height'] = 'Max height (px) [0 - no limit]';
|
||||||
|
$_['entry_jpg'] = 'JPG Quality (0-100)';
|
||||||
|
$_['entry_png'] = 'PNG Compression (0-9)';
|
||||||
|
$_['entry_webp'] = 'WebP Quality (0-100)';
|
||||||
|
$_['entry_targets'] = 'What to optimize?';
|
||||||
|
$_['text_all_catalog'] = '[ Entire image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Files only in image/catalog root';
|
||||||
|
$_['entry_threshold'] = 'Replacement threshold (%)';
|
||||||
|
$_['help_threshold'] = '0: replace only if file is smaller. 10: allow up to 10% file size increase.';
|
||||||
|
$_['entry_token'] = 'Activation Token (Root Domain)';
|
||||||
|
$_['entry_status'] = 'Module Status';
|
||||||
|
$_['entry_menu_position'] = 'Menu Position [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Modules List Only';
|
||||||
|
$_['text_menu_header'] = 'Top Header';
|
||||||
|
$_['text_menu_sidebar'] = 'Sidebar Menu';
|
||||||
|
$_['text_menu_both'] = 'Both (Header + Sidebar)';
|
||||||
|
$_['text_status_on'] = 'Enabled';
|
||||||
|
$_['text_status_off'] = 'Disabled';
|
||||||
|
$_['entry_engine'] = 'Image Processing Engine';
|
||||||
|
$_['text_engine_gd'] = 'GD (Standard)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (High quality, sRGB profile keeping)';
|
||||||
|
$_['entry_license'] = 'License';
|
||||||
|
$_['text_cleaner_info'] = 'This tool looks for files that physically exist on the server but are not linked in the database or template files.';
|
||||||
|
$_['entry_clean_targets'] = 'Folders to scan for junk?';
|
||||||
|
$_['entry_extended_log'] = 'Extended log (show used photos and their locations)';
|
||||||
|
$_['entry_process_broken'] = 'Include files with broken encoding ("?" in names)';
|
||||||
|
$_['button_scan_trash'] = 'FIND JUNK FILES';
|
||||||
|
$_['button_quarantine'] = 'Move to Quarantine (Safe)';
|
||||||
|
$_['button_delete_trash'] = 'Delete Permanently';
|
||||||
|
$_['button_empty_quarantine'] = 'Empty Quarantine Folder';
|
||||||
|
$_['button_restore_quarantine'] = 'Restore from Quarantine';
|
||||||
|
$_['text_scan_result'] = 'Scan Results';
|
||||||
|
$_['text_trash_files'] = 'Junk files found: ';
|
||||||
|
$_['text_trash_size'] = 'Potential space to free up: ';
|
||||||
|
$_['button_start'] = 'START OPTIMIZATION';
|
||||||
|
$_['button_pause'] = 'PAUSE';
|
||||||
|
$_['button_stop'] = 'STOP';
|
||||||
|
$_['button_continue'] = 'CONTINUE';
|
||||||
|
$_['button_start_new'] = 'START (New Run)';
|
||||||
|
$_['button_download_log'] = 'Download TXT Log';
|
||||||
|
$_['button_download'] = 'Download';
|
||||||
|
$_['button_save'] = 'Save';
|
||||||
|
$_['button_cancel'] = 'Cancel';
|
||||||
|
$_['text_progress'] = 'Progress:';
|
||||||
|
$_['text_old_size'] = 'Before:';
|
||||||
|
$_['text_new_size'] = 'After:';
|
||||||
|
$_['text_savings'] = 'Saved:';
|
||||||
|
$_['text_detailed_log'] = 'Detailed Log';
|
||||||
|
$_['text_page'] = 'Page';
|
||||||
|
$_['text_waiting'] = 'Waiting to start...';
|
||||||
|
$_['text_enabled'] = 'Enabled';
|
||||||
|
$_['text_disabled_error'] = 'Disabled (ERROR)';
|
||||||
|
$_['text_supported'] = 'Supported';
|
||||||
|
$_['text_no'] = 'No';
|
||||||
|
$_['error_permission'] = 'You do not have permission to modify this module!';
|
||||||
|
$_['error_license'] = 'Access Error: Invalid activation token for this domain!';
|
||||||
|
$_['error_domain'] = ' (Domain: ';
|
||||||
|
$_['text_found'] = 'Files found: ';
|
||||||
|
$_['text_compressed'] = 'Compressed';
|
||||||
|
$_['text_original'] = 'Original';
|
||||||
|
$_['text_error_process'] = 'Processing error: ';
|
||||||
|
$_['error_log_empty'] = 'Log is empty! Run the process first.';
|
||||||
|
$_['text_report_header'] = '=== REPORT ===';
|
||||||
|
$_['text_total_files'] = 'Total files: ';
|
||||||
|
$_['text_searching'] = 'Searching for files...';
|
||||||
|
$_['error_ajax'] = 'Error: ';
|
||||||
|
$_['text_resumed'] = '▶ PROCESS RESUMED...';
|
||||||
|
$_['text_paused'] = '⏸ PAUSED. Process suspended.';
|
||||||
|
$_['confirm_stop'] = 'Are you sure you want to completely abort the process?';
|
||||||
|
$_['confirm_delete'] = 'WARNING! Files will be permanently deleted from the server with no chance of recovery! Continue?';
|
||||||
|
$_['confirm_empty_q'] = 'Are you sure you want to completely empty the quarantine folder?';
|
||||||
|
$_['confirm_restore'] = 'All files from quarantine will be returned to their original locations. Continue?';
|
||||||
|
$_['button_stopping'] = 'Stopping...';
|
||||||
|
$_['text_stopped_user'] = '🛑 PROCESS ABORTED BY USER!';
|
||||||
|
$_['text_increase'] = 'Increased';
|
||||||
|
$_['text_done'] = '✅ DONE! Process completed.';
|
||||||
|
$_['error_timeout'] = 'Server timeout. Auto-retrying in 3 seconds...';
|
||||||
|
$_['text_building_index'] = 'Step 1/2: Indexing DB and templates (searching for used photos)...';
|
||||||
|
$_['text_comparing'] = 'Step 2/2: Comparing physical files with the index...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Quarantined to image/catalog_trash/ successfully!';
|
||||||
|
$_['text_deleted_done'] = '✅ Junk deletion completed successfully!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Quarantine folder emptied successfully!';
|
||||||
|
$_['text_restore_done'] = '✅ Files restored from quarantine to their original locations!';
|
||||||
|
$_['text_quarantine_move'] = 'Quarantined: ';
|
||||||
|
$_['text_deleted_file'] = 'Deleted: ';
|
||||||
|
$_['text_no_trash'] = 'No junk found in selected folders! All files are in use.';
|
||||||
|
$_['text_scanning_dirs'] = 'Scanning directories: ';
|
||||||
|
$_['text_log_used'] = '[IN USE]';
|
||||||
|
$_['text_log_trash'] = '[JUNK]';
|
||||||
|
$_['text_log_deleted'] = '[DELETED]';
|
||||||
|
$_['text_log_del_err'] = '[DELETE ERROR]';
|
||||||
|
$_['text_log_quarantine'] = '[QUARANTINED]';
|
||||||
|
$_['text_log_q_err'] = '[MOVE ERROR]';
|
||||||
|
$_['text_log_restored'] = '[RESTORED]';
|
||||||
|
$_['text_log_rest_err'] = '[RESTORE ERROR]';
|
||||||
|
$_['text_used_in'] = 'in: ';
|
||||||
|
$_['text_analyzing'] = 'Analyzing database...';
|
||||||
|
$_['text_clean_empty'] = 'Clean! No junk found.';
|
||||||
|
$_['text_processing'] = 'Processing...';
|
||||||
|
$_['text_restoring'] = 'Restoring files...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (And all subfolders)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Root only)';
|
||||||
|
$_['text_q_empty_err'] = 'Quarantine folder is empty or does not exist.';
|
||||||
|
$_['error_token'] = 'Invalid token. Please refresh the page.';
|
||||||
|
$_['text_apply_title'] = 'Optimization completed in folder: ';
|
||||||
|
$_['text_apply_info'] = 'Check the result. If everything looks good, apply changes to the main catalog.';
|
||||||
|
$_['entry_apply_backup'] = 'Move and create a backup (in Quarantine)';
|
||||||
|
$_['entry_apply_replace'] = 'Replace originals permanently';
|
||||||
|
$_['button_apply_main'] = 'APPLY TO MAIN CATALOG';
|
||||||
|
$_['button_delete_copies'] = 'Delete temporary copies (Cancel)';
|
||||||
|
$_['button_full_cleanup'] = 'Full cleanup of all temp folders and backups';
|
||||||
|
$_['text_apply_success'] = '✅ Files successfully moved. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Originals saved in folder: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Originals deleted.';
|
||||||
|
$_['text_error_source'] = 'Error: Folder with optimized files not found.';
|
||||||
|
$_['confirm_full_cleanup'] = 'WARNING! This action will permanently delete ALL backup folders (backup_*) and temp copy folders. Continue?';
|
||||||
|
$_['text_tools_desc'] = 'Global scanners and tools for file system and database image optimization.';
|
||||||
|
$_['text_tool_select'] = 'Select Tool:';
|
||||||
|
$_['text_group_scanners'] = 'Scanners & Analysis';
|
||||||
|
$_['text_group_generators'] = 'Generators & Processing';
|
||||||
|
$_['text_group_experimental'] = 'Experimental Features (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Warning: Experimental features modify system files or the database. Backup is highly recommended!';
|
||||||
|
$_['text_tool_broken_db'] = 'Broken/Empty DB Image Scanner';
|
||||||
|
$_['text_tool_watermark'] = 'Dynamic Watermark (Cache only)';
|
||||||
|
$_['text_tool_duplicates'] = 'Duplicate Finder (MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Heavy PNG to JPG Converter (non-transparent PNG scanner)';
|
||||||
|
$_['text_tool_placeholders'] = 'Placeholder Generator (Replaces No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Clean EXIF Data (Geotags/Meta)';
|
||||||
|
$_['text_tool_small_photos'] = 'Too Small Photo Scanner';
|
||||||
|
$_['text_tool_html_broken'] = 'Broken Images in HTML';
|
||||||
|
$_['text_tool_empty_folders'] = 'Remove Empty Folders (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Smart Cache Cleanup';
|
||||||
|
$_['text_tool_folder_tree'] = 'Folder Tree Statistics';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (No DB)';
|
||||||
|
$_['text_tool_translit'] = 'File Name Transliteration';
|
||||||
|
$_['text_tool_cache_restore'] = 'Restore Originals from Cache';
|
||||||
|
$_['entry_wm_type'] = 'Watermark Type';
|
||||||
|
$_['text_wm_image'] = 'Image (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Text';
|
||||||
|
$_['entry_wm_image_path'] = 'Image Path';
|
||||||
|
$_['entry_wm_text_val'] = 'Watermark Text';
|
||||||
|
$_['entry_wm_position'] = 'Position (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Opacity (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Only for Categories';
|
||||||
|
$_['entry_wm_brand'] = 'Only for Brands';
|
||||||
|
$_['entry_min_width'] = 'Minimum Width (px)';
|
||||||
|
$_['entry_min_height'] = 'Minimum Height (px)';
|
||||||
|
$_['entry_ph_product'] = 'Placeholder for Products';
|
||||||
|
$_['entry_ph_category'] = 'Placeholder for Categories';
|
||||||
|
$_['entry_ph_brand'] = 'Placeholder for Brands';
|
||||||
|
$_['button_run_tool'] = 'Run Tool';
|
||||||
|
$_['button_scan_tool'] = 'Scan';
|
||||||
|
$_['button_fix_tool'] = 'Fix Found';
|
||||||
|
$_['text_th_path'] = 'File / Path';
|
||||||
|
$_['text_th_size'] = 'Size';
|
||||||
|
$_['text_th_action'] = 'Action';
|
||||||
|
$_['text_th_status'] = 'Status';
|
||||||
|
$_['text_th_problem'] = 'Problem / Value';
|
||||||
|
$_['text_cron_title'] = 'Cron Settings';
|
||||||
|
$_['text_cron_desc'] = 'Add the command to your hosting Cron for automatic background compression.';
|
||||||
|
$_['entry_cron_new_only'] = 'Process only new (unprocessed) files';
|
||||||
|
$_['entry_cron_folders'] = 'Folders to scan (leave empty for entire catalog)';
|
||||||
|
$_['text_promo_title'] = 'Other [NAT] Series Modules';
|
||||||
|
$_['text_promo_desc'] = '[NAT] Series Modules are tools for deep OpenCart optimization. We focus on automation, DB speed, and server cleanup. All modules are open-source and share the same core logic.';
|
||||||
|
$_['text_support'] = 'Technical support and suggestions:';
|
||||||
|
$_['button_more'] = 'View all extensions on OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Hide empty categories/brands + SEO sorting (in stock first) + DB junk cleanup.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Sort by exact date';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Automated SEO page for new arrivals. Displays items strictly by the real date added. Sliders, grid, and smart date archive sidebar.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Current Module.</strong> Compress and resize ORIGINAL photos (image/catalog) to WebP + Server cleanup of unused images.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Find and clean external links and HTTP images in DB and theme files. Ideal for HTTPS migration or cleanup after parsing.';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Create polls and voting widgets with a visual design builder, deep statistics, and cheat protection.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Intelligent garbage cleanup in the database, system log management, and crucial index addition for speed boost.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Import/export prices and stock levels from/to files (xls, xlsx, csv, xml, json) or by url. Field mapping, Cron automation, text utilities.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Handy administrator tools: login as customer, admin customization, user permissions management, and hiding unnecessary menu items.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Automatic tracking of UTM tags during checkout. Saves the traffic source, campaign, keywords, and displays this data in order details to analyze advertising effectiveness.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Upper promotional info-banner, information bar above header, marquee and top-banner';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Create interactive promo bars and header slideshows with a block builder, countdown timers, subscription forms, and flexible targeting.';
|
||||||
|
$_['text_tool_no_image'] = 'Products without main image (empty in DB)';
|
||||||
|
$_['text_problem_main_broken'] = 'Broken main image (file deleted)';
|
||||||
|
$_['text_problem_add_broken'] = 'Broken additional image (file deleted)';
|
||||||
|
$_['text_problem_no_image'] = 'Missing main image';
|
||||||
|
$_['text_err_dir'] = 'Error: Directory not found or invalid path.';
|
||||||
|
$_['text_err_no_queue'] = 'Error: No queue found. Run scan first.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Deleted empty folder: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'No empty folders found in image/catalog/.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Total empty folders removed: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Image cache has been cleared.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Watermarks are operating in SMART mode: originals remain untouched, applied only on cache generation.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Scanning image/catalog/ for MD5 duplicates... This may take a while.';
|
||||||
|
$_['text_log_found_dupe'] = 'Found duplicate: ';
|
||||||
|
$_['text_log_no_dupes'] = 'No duplicates found.';
|
||||||
|
$_['text_log_total_dupes'] = 'Found duplicates: %s. Potential savings: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Click "Fix Found" to redirect DB links to original and delete duplicates.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Fixed and removed duplicate: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Successfully fixed %s duplicates. DB updated.';
|
||||||
|
$_['text_log_scan_png'] = 'Scanning for heavy PNG files without transparency...';
|
||||||
|
$_['text_log_no_png'] = 'No suitable PNG files found.';
|
||||||
|
$_['text_log_total_png'] = 'Found %s PNG without transparency. Click "Fix Found" to convert to JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Converted to JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Successfully converted %s files. Space saved: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Updated %s Products with placeholder.';
|
||||||
|
$_['text_log_ph_cat'] = 'Updated %s Categories with placeholder.';
|
||||||
|
$_['text_log_ph_brand'] = 'Updated %s Brands with placeholder.';
|
||||||
|
$_['text_log_ph_empty'] = 'No placeholders selected in settings.';
|
||||||
|
$_['text_log_scan_exif'] = 'Scanning for EXIF data...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Stripped EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'No EXIF data found to clean.';
|
||||||
|
$_['text_log_success_exif'] = 'Cleaned EXIF from %s files. Saved: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Scanning for photos smaller than %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Too small (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'No small photos found.';
|
||||||
|
$_['text_log_total_small'] = 'Found %s small photos. Please review and re-upload.';
|
||||||
|
$_['text_log_html_broken'] = 'Broken IMG in HTML table %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'No broken images found in HTML descriptions.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Found %s broken links in HTML. Please edit them manually.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Scanning image/cache/ for orphaned files...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Deleted orphaned cache: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'No orphaned cache files found.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Removed %s orphaned cache files. Space saved: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Scanning for files with cyrillic or spaces...';
|
||||||
|
$_['text_log_no_bad_names'] = 'No files with bad names found.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Found %s bad files. Click "Fix Found" to transliterate and update DB.';
|
||||||
|
$_['text_log_renamed'] = 'Renamed: ';
|
||||||
|
$_['text_log_success_translit'] = 'Successfully transliterated %s files and updated DB.';
|
||||||
|
$_['text_log_scan_restore'] = 'Scanning image/cache/ to restore lost originals...';
|
||||||
|
$_['text_log_restored_cache'] = 'Restored from cache: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'No missing originals found in cache.';
|
||||||
|
$_['text_log_success_restore'] = 'Successfully restored %s original images from cache.';
|
||||||
|
$_['error_invalid_folder'] = 'Error: Invalid folder name!';
|
||||||
|
$_['text_formats_title'] = 'Modern Formats (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Settings for integrating modern formats into the OpenCart file system and templates.';
|
||||||
|
$_['entry_support_svg'] = 'SVG Upload Support';
|
||||||
|
$_['help_support_svg'] = 'Allows uploading vector SVG images via the standard Filemanager and safely displaying them on the storefront without size distortion.';
|
||||||
|
$_['entry_support_webp'] = 'WebP Upload Support';
|
||||||
|
$_['help_support_webp'] = 'Allows manually uploading, linking, and displaying ready .webp files on the website via the Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP in TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Adds WebP upload, preview, and thumbnail support to sitecreator\'s TrueFileManager (if installed).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Ultra-fast replacement: automatically changes .jpg/.png extensions to .webp directly in the storefront HTML output and adds the loading="lazy" attribute. Does not touch the database!';
|
||||||
|
$_['help_tool_broken_db'] = 'Finds images in the database whose physical files have been deleted from the server. Clears broken links.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Recursively scans image/catalog/ and safely removes all folders that contain no files.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Builds a directory tree and shows the real size of each folder to find space eaters.';
|
||||||
|
$_['help_tool_html_broken'] = 'Scans HTML descriptions of products and categories for <img> tags with non-existent images.';
|
||||||
|
$_['help_tool_small_photos'] = 'Finds pictures with resolution below the specified one. Added search links to Google and Yandex Images so you can easily find high-quality replacements.';
|
||||||
|
$_['help_tool_duplicates'] = 'Searches for exact duplicates by MD5 hash (different names, same image). Merges them into one file and updates the DB.';
|
||||||
|
$_['help_tool_watermark'] = 'Applies watermarks in SMART mode (only to storefront cache, originals remain unchanged).';
|
||||||
|
$_['help_tool_placeholders'] = 'Massively sets the selected placeholder image for products and categories that have no photo.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Scans for heavy PNG files without transparency (alpha channel). Since they do not need transparency, they can be safely converted to lightweight JPG. This greatly reduces file size (typically by 70-80%) and speeds up your website, automatically updating all links in the database.';
|
||||||
|
$_['help_tool_exif'] = 'Scans JPEG files and removes hidden EXIF data (geotags, camera data), reducing size by 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Finds cached files in image/cache/ whose originals have already been deleted, and removes this trash.';
|
||||||
|
$_['help_tool_translit'] = 'Finds files with Cyrillic or spaces, renames them using transliteration, and updates paths in the DB.';
|
||||||
|
$_['help_tool_cache_restore'] = 'If you accidentally deleted an original from catalog, this tool will try to find its copy in cache and restore it.';
|
||||||
|
$_['entry_quarantine'] = 'Move originals to Quarantine (instead of deleting)';
|
||||||
|
$_['entry_backup_db'] = 'Create database backup of modified tables in SQL before starting';
|
||||||
|
$_['button_fix_selected'] = 'FIX SELECTED';
|
||||||
|
$_['button_export_log'] = 'Export to CSV';
|
||||||
|
$_['text_th_before'] = 'State BEFORE';
|
||||||
|
$_['text_th_after'] = 'Expected result AFTER';
|
||||||
|
$_['text_th_select'] = 'Action';
|
||||||
|
$_['text_th_preview'] = 'Preview';
|
||||||
|
$_['text_th_name'] = 'Object / File';
|
||||||
|
$_['text_global_action'] = 'Bulk action for selected:';
|
||||||
|
$_['text_action_clear'] = 'Clear DB Link';
|
||||||
|
$_['text_action_disable'] = 'Disable (Status = 0)';
|
||||||
|
$_['text_action_stock'] = 'Set Out of Stock';
|
||||||
|
$_['text_action_placeholder'] = 'Set Placeholder';
|
||||||
|
$_['text_action_ignore'] = 'Do nothing';
|
||||||
|
$_['text_action_merge'] = 'Merge (delete duplicate, keep original)';
|
||||||
|
$_['text_action_convert'] = 'Convert to JPG';
|
||||||
|
$_['text_action_rename'] = 'Rename to translit';
|
||||||
|
$_['text_action_delete'] = 'Delete';
|
||||||
|
$_['text_action_restore'] = 'Restore';
|
||||||
|
$_['text_db_backup_created'] = 'Database backup created: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Quarantined to: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Successfully executed';
|
||||||
|
$_['text_fix_status_error'] = 'Error: %s';
|
||||||
|
$_['text_log_exported'] = 'Log successfully exported!';
|
||||||
|
$_['text_select_action'] = '-- Select Action --';
|
||||||
|
$_['error_empty_selection'] = 'Error: No rows selected for fixing!';
|
||||||
|
$_['text_link_admin'] = 'Admin';
|
||||||
|
$_['text_link_catalog'] = 'Store';
|
||||||
|
$_['entry_threads'] = 'Processing Threads';
|
||||||
|
$_['text_threads_optimal'] = 'optimal';
|
||||||
|
$_['button_download_zip'] = 'Download ZIP and Delete Folder';
|
||||||
|
$_['confirm_download_and_delete'] = 'Did the ZIP download successfully? Can we delete the temporary copies folder on the server?';
|
||||||
|
$_['button_clear_backups'] = 'Delete DB Backups';
|
||||||
|
$_['button_clear_backups_short'] = 'Clear Backups';
|
||||||
|
$_['confirm_clear_backups'] = 'Are you sure you want to delete all SQL database backups?';
|
||||||
|
$_['text_compare_title'] = 'Compare Images BEFORE / AFTER';
|
||||||
|
$_['text_compare_before'] = 'BEFORE';
|
||||||
|
$_['text_compare_after'] = 'AFTER';
|
||||||
|
$_['text_backups_cleared'] = 'All database backups deleted successfully!';
|
||||||
|
$_['error_no_backups'] = 'No backups found or folder is empty.';
|
||||||
|
$_['button_restore'] = 'Restore';
|
||||||
|
$_['button_empty'] = 'Clear';
|
||||||
|
$_['entry_wm_targets'] = 'Apply watermark to:';
|
||||||
|
$_['text_wm_target_product'] = 'Product Photos';
|
||||||
|
$_['text_wm_target_category'] = 'Category Photos';
|
||||||
|
$_['text_wm_target_brand'] = 'Brand Logos';
|
||||||
|
$_['text_wm_target_banner'] = 'Banners and Slides';
|
||||||
|
$_['text_wm_target_blog'] = 'Articles / Blog';
|
||||||
|
$_['error_no_scan_data'] = 'Error: No scan data found. Please run scan first!';
|
||||||
|
$_['error_row_not_found'] = 'Error: Row not found in scan results!';
|
||||||
|
$_['error_file_not_found'] = 'Error: File not found on server!';
|
||||||
|
$_['text_folder_rules_title'] = 'Individual Folder Rules';
|
||||||
|
$_['text_folder_rules_desc'] = 'You can override compression quality and maximum sizes for specific folders. Leave a field blank to use global settings. The path is relative to the image/ directory (e.g. catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Folder Path';
|
||||||
|
$_['entry_jpg_quality'] = 'JPG Quality';
|
||||||
|
$_['entry_webp_quality'] = 'WebP Quality';
|
||||||
|
$_['button_add_rule'] = 'Add Rule';
|
||||||
|
$_['button_remove'] = 'Remove';
|
||||||
|
$_['entry_wm_angle'] = 'Watermark Rotation Angle (degrees)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Watermark Image Corner Radius (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Watermark Scaling';
|
||||||
|
$_['entry_wm_size_percent'] = 'Watermark Size (% of image width)';
|
||||||
|
$_['entry_wm_text_color'] = 'Watermark Text Color';
|
||||||
|
$_['entry_wm_text_font'] = 'Watermark Font (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Categories/Brands Filter Mode';
|
||||||
|
$_['text_wm_filter_include'] = 'Only for Selected';
|
||||||
|
$_['text_wm_filter_exclude'] = 'For All Except Selected';
|
||||||
|
$_['text_wm_size_original'] = 'Original Size';
|
||||||
|
$_['text_wm_size_percent'] = 'Proportional to image width (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Preview Watermark';
|
||||||
|
$_['text_wm_preview_title'] = 'Watermark Preview';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Exclude folders from scan';
|
||||||
|
$_['text_cron_entities'] = 'Which photo types to process?';
|
||||||
|
$_['text_cron_quality_override'] = 'Override compression settings for Cron';
|
||||||
|
$_['text_cron_recommendations'] = 'Recommended Cron Schedule';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>For small sites (up to 5000 products)</b>: run Cron every hour (<code>0 * * * *</code>).</li><li><b>For medium sites (5000-20000 products)</b>: run every 3-6 hours (<code>0 */6 * * *</code>).</li><li><b>For large sites (over 20000 products)</b>: run once a day at night (e.g. at 2:00 AM: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Server Broken/Empty Files Scanner';
|
||||||
|
$_['help_tool_broken_files'] = 'Scans physical files in image/catalog/ for corrupted images or empty files of 0 bytes.';
|
||||||
|
$_['text_grid_broken_file'] = 'Broken or empty file on server';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Delete or replace with placeholder';
|
||||||
|
$_['text_description_broken_files'] = 'Scanning results of physical files in image/catalog/. Shows broken (corrupted) images or files with 0 bytes size.';
|
||||||
|
$_['text_action_strip'] = 'Strip EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Set Placeholder';
|
||||||
|
$_['text_th_entity'] = 'Entity / Related Links';
|
||||||
|
$_['text_cron_entity_product'] = 'Products';
|
||||||
|
$_['text_cron_entity_category'] = 'Categories';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Manufacturers / Brands';
|
||||||
|
$_['text_cron_entity_banner'] = 'Banners';
|
||||||
|
$_['text_cron_entity_blog'] = 'Blog / Articles';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'JPG Quality (Cron)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'WebP Quality (Cron)';
|
||||||
|
$_['entry_cron_max_width'] = 'Max Width px (Cron)';
|
||||||
|
$_['entry_cron_max_height'] = 'Max Height px (Cron)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Folders to scan';
|
||||||
|
$_['text_no_fonts'] = 'No TTF fonts found in system/library/font/';
|
||||||
|
$_['button_apply'] = 'Apply';
|
||||||
|
$_['text_backup_will_be_created'] = 'A database backup will be automatically created before running this action.';
|
||||||
|
$_['text_bulk_action'] = 'Bulk action for selected';
|
||||||
|
$_['text_grid_results'] = 'Scanner Results & Details';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Original files will be moved to quarantine (image/catalog_trash/) before modification.';
|
||||||
|
$_['text_success_apply'] = 'Changes successfully applied!';
|
||||||
|
$_['text_description_broken'] = 'Database scan results for broken links (table records referencing non-existent files).';
|
||||||
|
$_['text_description_cache_restore'] = 'Results of search for cache files whose originals are missing in the catalog.';
|
||||||
|
$_['text_description_duplicates'] = 'Results of search for exact duplicate images by MD5 hash.';
|
||||||
|
$_['text_description_empty_folders'] = 'List of empty folders in image/catalog/ directory for deletion.';
|
||||||
|
$_['text_description_exif'] = 'Results of search for images containing EXIF metadata that can be stripped.';
|
||||||
|
$_['text_description_folder_tree'] = 'Folder size statistics in image/catalog/ directory.';
|
||||||
|
$_['text_description_html_broken'] = 'Results of search for non-existent image files referenced in HTML descriptions.';
|
||||||
|
$_['text_description_placeholders'] = 'Results of search for entities without images to fill with placeholders.';
|
||||||
|
$_['text_description_png_jpg'] = 'Results of search for heavy PNG images without transparency that can be compressed to JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'List of images with dimensions smaller than the minimum specified (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Results of search for cache files whose original images have already been deleted.';
|
||||||
|
$_['text_description_translit'] = 'Results of search for files with Cyrillic characters or spaces in their names.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Clear link or set placeholder';
|
||||||
|
$_['text_grid_dupe_after'] = 'Original (remains)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Duplicate (will be deleted)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Empty folder';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Delete empty folder';
|
||||||
|
$_['text_grid_exif_after'] = 'Strip metadata';
|
||||||
|
$_['text_grid_exif_before'] = 'Contains EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Fix link manually';
|
||||||
|
$_['text_grid_missing_file'] = 'File is missing';
|
||||||
|
$_['text_grid_missing_html'] = 'Image not found in description';
|
||||||
|
$_['text_grid_missing_orig'] = 'Original is missing';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Restore original from cache';
|
||||||
|
$_['text_grid_no_image'] = 'No image (empty)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Orphaned cache';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Delete unneeded cache';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Set brand placeholder';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Set category placeholder';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Set product placeholder';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Recommended to replace with larger one';
|
||||||
|
$_['text_grid_too_small'] = 'Too small';
|
||||||
|
$_['text_no_relations'] = 'No database relations';
|
||||||
|
$_['text_action_archive'] = 'Archive (to ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Archive + Delete';
|
||||||
|
$_['text_tool_sanitizer'] = 'File Name Sanitizer';
|
||||||
|
$_['help_tool_sanitizer'] = 'Bulk cleanup of file and folder names from Cyrillic, spaces and special characters, normalization of extensions.';
|
||||||
|
$_['text_description_sanitizer'] = 'Results of file name analysis for sanitization based on selected rules.';
|
||||||
|
$_['entry_sz_translit'] = 'Transliterate (Cyrillic to Latin)';
|
||||||
|
$_['entry_sz_spaces'] = 'Replace spaces with "_"';
|
||||||
|
$_['entry_sz_special'] = 'Remove special characters';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Lowercase file name';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Lowercase extension';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Normalize extension (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Folder filtering mode';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Scan all folders except selected (Exceptions)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Scan only selected folders';
|
||||||
|
$_['button_select_all'] = 'Select All';
|
||||||
|
$_['button_deselect_all'] = 'Deselect All';
|
||||||
|
$_['entry_wm_upload_font'] = 'Upload custom TTF font';
|
||||||
|
$_['button_upload_font'] = 'Upload Font';
|
||||||
|
$_['text_success_font_upload'] = 'Font successfully uploaded!';
|
||||||
|
$_['error_font_upload'] = 'Error uploading font! Only .ttf files up to 5 MB are allowed.';
|
||||||
|
$_['entry_lazy_load'] = 'Progressive Lazy Load';
|
||||||
|
$_['help_lazy_load'] = 'Enables a premium progressive "blur-up" lazy loading on the frontend. Images are replaced with tiny blurred versions and loaded fully when scrolled into view.';
|
||||||
|
$_['confirm_replace_mode'] = 'WARNING! You have selected the "Replace originals" mode. All images will be overwritten directly on the server. We strongly recommend making a backup first. Are you sure you want to continue?';
|
||||||
|
$_['text_compare'] = 'Compare';
|
||||||
|
$_['text_home'] = 'Home';
|
||||||
|
$_['text_tech_gd'] = 'GD Library';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'WebP Support';
|
||||||
|
$_['text_position_1'] = '1 - Top Left';
|
||||||
|
$_['text_position_2'] = '2 - Top Center';
|
||||||
|
$_['text_position_3'] = '3 - Top Right';
|
||||||
|
$_['text_position_4'] = '4 - Middle Left';
|
||||||
|
$_['text_position_5'] = '5 - Center';
|
||||||
|
$_['text_position_6'] = '6 - Middle Right';
|
||||||
|
$_['text_position_7'] = '7 - Bottom Left';
|
||||||
|
$_['text_position_8'] = '8 - Bottom Center';
|
||||||
|
$_['text_position_9'] = '9 - Bottom Right';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Log Level';
|
||||||
|
$_['text_log_changed_only'] = 'Changed files only';
|
||||||
|
$_['text_log_all'] = 'All files (verbose)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Skipped (no improvement)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Reset optimization cache (force full rescan)';
|
||||||
|
$_['confirm_reset_cache'] = 'Reset optimization cache? Next scan will check ALL files again.';
|
||||||
|
$_['text_cache_cleared'] = 'Optimization cache cleared. Next scan will check all files.';
|
||||||
|
$_['error_wm_preview'] = 'Failed to generate watermark preview.';
|
||||||
|
$_['error_wm_too_large'] = 'Watermark image is too large (%dx%d px)! Maximum allowed size is %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'WARNING: Some files could not be replaced (locked or permission denied):';
|
||||||
|
$_['text_action_compress'] = 'Compress Images';
|
||||||
|
$_['text_estimated'] = 'Estimated';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Min savings (bytes)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Min savings (%)';
|
||||||
|
$_['text_optimized'] = 'Optimized';
|
||||||
|
$_['entry_broken_fallback'] = 'Broken Image Fallback';
|
||||||
|
$_['help_broken_fallback'] = 'Automatically replaces missing or corrupted catalog images on the storefront with a fallback image.';
|
||||||
|
$_['entry_fallback_image'] = 'Fallback Image';
|
||||||
|
$_['help_fallback_image'] = 'Select a custom image to display when a product or category image is broken. Defaults to no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'CMYK to RGB Converter';
|
||||||
|
$_['help_tool_cmyk'] = 'Convert JPEG images from CMYK color profile to sRGB to ensure correct display on Safari and iOS devices.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Top Heavy Files Scanner';
|
||||||
|
$_['help_tool_heavy_files'] = 'Scan and find the heaviest original files in catalog directory and optimize them directly.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Max files to find (total limit)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Convert to RGB';
|
||||||
|
$_['text_description_cmyk'] = 'CMYK JPEGs found. Conversion to sRGB guarantees they are displayed correctly on all platforms, including iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'List of the top %d heaviest files in the image directory. You can optimize them here directly.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Status: All folders will be scanned (no exclusions).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Status: All folders will be scanned EXCEPT the %d checked folders.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Status: Only the %d checked folders will be scanned.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'WARNING: No folders are selected! The Cron job will scan nothing.';
|
||||||
|
$_['text_action_apply_changes'] = 'Apply specified changes';
|
||||||
|
$_['entry_wm_status'] = 'Watermark Status';
|
||||||
|
$_['text_disabled'] = 'Disabled';
|
||||||
|
$_['entry_rule_name'] = 'Rule Name';
|
||||||
|
$_['entry_rule_status'] = 'Rule Status';
|
||||||
|
$_['entry_action'] = 'Action';
|
||||||
|
$_['entry_ph_status'] = 'Placeholder Status';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Max files to scan (limit)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Min Size Threshold';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Max files to scan (limit)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Min Size Threshold';
|
||||||
|
$_['text_wm_rule_title'] = 'Watermark Rule';
|
||||||
|
$_['button_close'] = 'Close';
|
||||||
|
$_['text_ph_rule_title'] = 'Placeholder Rule';
|
||||||
|
$_['entry_ph_image'] = 'Placeholder Image';
|
||||||
|
$_['button_clear_cache_wm'] = 'Clear Cache (apply watermarks)';
|
||||||
|
$_['text_active'] = 'Active';
|
||||||
|
$_['text_error'] = 'Not Active';
|
||||||
|
$_['text_th_dupe_delete'] = 'File to delete (Duplicate)';
|
||||||
|
$_['text_th_dupe_keep'] = 'File to keep (Original)';
|
||||||
|
$_['text_dupe_original_label'] = 'Original';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Duplicate';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Disk Usage';
|
||||||
|
$_['text_cumulative_stats'] = 'Optimization Statistics';
|
||||||
|
$_['text_disk_catalog'] = 'Catalog images';
|
||||||
|
$_['text_disk_cache'] = 'OC Cache';
|
||||||
|
$_['text_disk_trash'] = 'Module trash';
|
||||||
|
$_['text_disk_other'] = 'Other';
|
||||||
|
$_['text_disk_free'] = 'Free';
|
||||||
|
$_['text_total_files_opt'] = 'Files optimized';
|
||||||
|
$_['text_total_saved'] = 'Total saved';
|
||||||
|
$_['text_avg_saving_pct'] = 'Avg. savings';
|
||||||
|
$_['text_stats_hint'] = 'Statistics accumulate across all optimization sessions (stored locally)';
|
||||||
|
$_['text_reset_stats'] = 'Reset statistics';
|
||||||
|
$_['text_reset_stats_confirm']= 'Are you sure you want to reset optimization statistics?';
|
||||||
|
$_['text_export_import'] = 'Export / Import Settings';
|
||||||
|
$_['button_export_settings'] = 'Export settings (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Import settings';
|
||||||
|
$_['help_export_settings'] = 'Download all module settings as a JSON file';
|
||||||
|
$_['help_import_settings'] = 'Upload a previously exported JSON to restore settings';
|
||||||
|
$_['text_import_success'] = 'Settings imported successfully. Reload the page to apply.';
|
||||||
|
$_['error_import_file'] = 'No file uploaded or upload error';
|
||||||
|
$_['error_import_invalid'] = 'Invalid settings file (wrong module or format)';
|
||||||
|
$_['text_click_to_load'] = 'Click ↑ to load disk usage';
|
||||||
|
$_['tab_formats'] = 'Image Formats';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Hosting Disk Quota Limits (manual)';
|
||||||
|
$_['text_disk_quota_desc'] = 'If the disk space graph shows incorrect data (the server\'s physical partition instead of your hosting account limit), you can specify your hosting quota limits manually. Enter 0 for auto-detect.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Total Hosting Disk Space (GB)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)';
|
||||||
|
$_['entry_auto_disk'] = 'Auto-update on page load';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Task Name';
|
||||||
|
$_['entry_cron_task_summary'] = 'Compression settings';
|
||||||
|
$_['button_add_cron_task'] = 'Add Cron Task';
|
||||||
|
$_['button_copy_cron_url'] = 'Copy URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'Cron task execution URL:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'License Key Required';
|
||||||
|
$_['text_license_required_desc'] = 'Please enter your license key to activate the module. All configuration and optimization features are locked until a valid key is provided.';
|
||||||
|
$_['entry_token_desc'] = 'Enter the activation token issued for your root domain. You can obtain it from your purchase page or by contacting support.';
|
||||||
|
$_['button_activate'] = 'Activate Module';
|
||||||
|
$_['text_support'] = 'Technical Support';
|
||||||
|
$_['text_support_desc'] = 'If you don\'t have a key yet, or face issues, please provide your order ID and domain name.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Watermark Max Width (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Watermark Max Height (px)';
|
||||||
|
$_['help_wm_max_width'] = 'If the source watermark image exceeds this width, it will be automatically scaled down. Set 0 or leave empty for default 800px.';
|
||||||
|
$_['help_wm_max_height'] = 'If the source watermark image exceeds this height, it will be automatically scaled down. Set 0 or leave empty for default 800px.';
|
||||||
|
$_['entry_disk_status'] = 'Enable Disk Space Scan & Graph';
|
||||||
|
$_['help_disk_status'] = 'If enabled, the module will periodically scan the image folder size and display a pie chart. Disable this on large sites to improve settings page load speed.';
|
||||||
|
$_['text_disk_settings_title'] = 'Disk Limits & Scanning Settings';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,565 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Сжатие оригиналов и удаление мусора';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Сжатие оригиналов и удаление мусора';
|
||||||
|
$_['text_extension'] = 'Расширения';
|
||||||
|
$_['text_edit'] = 'Настройки модуля';
|
||||||
|
$_['text_success'] = 'Настройки успешно сохранены!';
|
||||||
|
$_['entry_warning'] = 'ВНИМАНИЕ: Обязательно сделайте бэкап файлов сайта и особенно папки /image/catalog перед началом работы!';
|
||||||
|
$_['text_tech'] = 'Проверка технологий сервера:';
|
||||||
|
$_['text_author'] = 'Техническая поддержка и пожелания: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Оптимизация и Ресайз';
|
||||||
|
$_['tab_cleaner'] = 'Очистка неиспользуемых изображений';
|
||||||
|
$_['tab_info'] = 'Инфо / Экосистема [NAT]';
|
||||||
|
$_['tab_settings'] = 'Настройки';
|
||||||
|
$_['tab_tools'] = 'Инструменты (pro)';
|
||||||
|
$_['tab_cron'] = 'Крон-задачи';
|
||||||
|
$_['entry_mode'] = 'Режим работы';
|
||||||
|
$_['text_copy'] = 'Создать копию (безопасно)';
|
||||||
|
$_['text_replace'] = 'Заменять оригиналы (требует бэкап!)';
|
||||||
|
$_['entry_folder'] = 'Имя папки для копии';
|
||||||
|
$_['entry_batch'] = 'Файлов за проход';
|
||||||
|
$_['entry_log_limit'] = 'Строк лога на экране';
|
||||||
|
$_['entry_max_width'] = 'Макс. ширина (px) [0 - без лимита]';
|
||||||
|
$_['entry_max_height'] = 'Макс. высота (px) [0 - без лимита]';
|
||||||
|
$_['entry_jpg'] = 'Качество JPG (0-100)';
|
||||||
|
$_['entry_png'] = 'Сжатие PNG (0-9)';
|
||||||
|
$_['entry_webp'] = 'Качество WebP (0-100)';
|
||||||
|
$_['entry_targets'] = 'Что оптимизировать?';
|
||||||
|
$_['text_all_catalog'] = '[ Весь каталог image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Файлы только в корне image/catalog';
|
||||||
|
$_['entry_threshold'] = 'Порог замены (%)';
|
||||||
|
$_['help_threshold'] = '0: заменять только если вес стал меньше. 10: разрешить увеличение веса до 10%.';
|
||||||
|
$_['entry_token'] = 'Токен активации (на корень домена)';
|
||||||
|
$_['entry_status'] = 'Статус модуля';
|
||||||
|
$_['entry_menu_position'] = 'Позиция меню [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Только в модулях';
|
||||||
|
$_['text_menu_header'] = 'В шапке (Header)';
|
||||||
|
$_['text_menu_sidebar'] = 'В боковом меню (Sidebar)';
|
||||||
|
$_['text_menu_both'] = 'Везде (Шапка + Сайдбар)';
|
||||||
|
$_['text_status_on'] = 'Включено';
|
||||||
|
$_['text_status_off'] = 'Отключено';
|
||||||
|
$_['entry_engine'] = 'Движок обработки изображений';
|
||||||
|
$_['text_engine_gd'] = 'GD (Стандарт)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (Высокое качество, сохранение профилей sRGB)';
|
||||||
|
$_['entry_license'] = 'Лицензия';
|
||||||
|
$_['text_cleaner_info'] = 'Этот инструмент ищет файлы, которые физически существуют на диске, но нигде не используются в базе данных или файлах шаблона.';
|
||||||
|
$_['entry_clean_targets'] = 'В каких папках искать мусор?';
|
||||||
|
$_['entry_extended_log'] = 'Расширенный лог (показать используемые фото и где они найдены)';
|
||||||
|
$_['entry_process_broken'] = 'Учитывать файлы с битой кодировкой (имена с символом "?")';
|
||||||
|
$_['button_scan_trash'] = 'НАЙТИ МУСОР';
|
||||||
|
$_['button_quarantine'] = 'В Карантин (Безопасно)';
|
||||||
|
$_['button_delete_trash'] = 'Удалить навсегда';
|
||||||
|
$_['button_empty_quarantine'] = 'Очистить папку Карантина';
|
||||||
|
$_['button_restore_quarantine'] = 'Вернуть из Карантина';
|
||||||
|
$_['text_scan_result'] = 'Результаты сканирования';
|
||||||
|
$_['text_trash_files'] = 'Мусорных файлов: ';
|
||||||
|
$_['text_trash_size'] = 'Потенциально освободится: ';
|
||||||
|
$_['button_start'] = 'СТАРТ ОПТИМИЗАЦИИ';
|
||||||
|
$_['button_pause'] = 'ПАУЗА';
|
||||||
|
$_['button_stop'] = 'ОСТАНОВИТЬ';
|
||||||
|
$_['button_continue'] = 'ПРОДОЛЖИТЬ';
|
||||||
|
$_['button_start_new'] = 'СТАРТ (Новый запуск)';
|
||||||
|
$_['button_download_log'] = 'Скачать лог в TXT';
|
||||||
|
$_['button_download'] = 'Скачать';
|
||||||
|
$_['button_save'] = 'Сохранить';
|
||||||
|
$_['button_cancel'] = 'Отмена';
|
||||||
|
$_['text_progress'] = 'Прогресс:';
|
||||||
|
$_['text_old_size'] = 'Было:';
|
||||||
|
$_['text_new_size'] = 'Стало:';
|
||||||
|
$_['text_savings'] = 'Экономия:';
|
||||||
|
$_['text_detailed_log'] = 'Детальный лог';
|
||||||
|
$_['text_page'] = 'Стр.';
|
||||||
|
$_['text_waiting'] = 'Ожидание запуска...';
|
||||||
|
$_['text_enabled'] = 'Включено';
|
||||||
|
$_['text_disabled_error'] = 'Выключено (ОШИБКА)';
|
||||||
|
$_['text_supported'] = 'Поддерживается';
|
||||||
|
$_['text_no'] = 'Нет';
|
||||||
|
$_['error_permission'] = 'У вас нет прав для управления модулем!';
|
||||||
|
$_['error_license'] = 'Ошибка доступа: Неверный токен активации для этого домена!';
|
||||||
|
$_['error_domain'] = ' (Домен: ';
|
||||||
|
$_['text_found'] = 'Найдено файлов: ';
|
||||||
|
$_['text_compressed'] = 'Сжат';
|
||||||
|
$_['text_original'] = 'Оригинал';
|
||||||
|
$_['text_error_process'] = 'Ошибка обработки: ';
|
||||||
|
$_['error_log_empty'] = 'Лог пуст! Сначала запустите процесс.';
|
||||||
|
$_['text_report_header'] = '=== ОТЧЕТ ===';
|
||||||
|
$_['text_total_files'] = 'Всего файлов: ';
|
||||||
|
$_['text_searching'] = 'Поиск файлов...';
|
||||||
|
$_['error_ajax'] = 'Ошибка: ';
|
||||||
|
$_['text_resumed'] = '▶ ПРОЦЕСС ВОЗОБНОВЛЕН...';
|
||||||
|
$_['text_paused'] = '⏸ ПАУЗА. Процесс приостановлен.';
|
||||||
|
$_['confirm_stop'] = 'Вы уверены, что хотите полностью прервать процесс?';
|
||||||
|
$_['confirm_delete'] = 'ВНИМАНИЕ! Файлы будут удалены с сервера навсегда без возможности восстановления! Продолжить?';
|
||||||
|
$_['confirm_empty_q'] = 'Вы уверены, что хотите полностью очистить папку карантина?';
|
||||||
|
$_['confirm_restore'] = 'Все файлы из папки карантина будут возвращены на свои места. Продолжить?';
|
||||||
|
$_['button_stopping'] = 'Остановка...';
|
||||||
|
$_['text_stopped_user'] = '🛑 ПРОЦЕСС ПРЕРВАН ПОЛЬЗОВАТЕЛЕМ!';
|
||||||
|
$_['text_increase'] = 'Увеличение';
|
||||||
|
$_['text_done'] = '✅ ГОТОВО! Процесс завершен.';
|
||||||
|
$_['error_timeout'] = 'Таймаут сервера. Автоповтор через 3 секунды...';
|
||||||
|
$_['text_building_index'] = 'Шаг 1/2: Индексация БД и шаблонов (поиск используемых фото)...';
|
||||||
|
$_['text_comparing'] = 'Шаг 2/2: Сравнение физических файлов с индексом...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Процесс переноса в image/catalog_trash/ завершен!';
|
||||||
|
$_['text_deleted_done'] = '✅ Процесс удаления мусора завершен!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Папка карантина успешно удалена с сервера!';
|
||||||
|
$_['text_restore_done'] = '✅ Файлы успешно возвращены из карантина в рабочую папку!';
|
||||||
|
$_['text_quarantine_move'] = 'В карантине: ';
|
||||||
|
$_['text_deleted_file'] = 'Удален: ';
|
||||||
|
$_['text_no_trash'] = 'В выбранных папках мусор не найден! Все файлы используются.';
|
||||||
|
$_['text_scanning_dirs'] = 'Сканируем директории: ';
|
||||||
|
$_['text_log_used'] = '[ИСПОЛЬЗУЕТСЯ]';
|
||||||
|
$_['text_log_trash'] = '[МУСОР]';
|
||||||
|
$_['text_log_deleted'] = '[УДАЛЕНО]';
|
||||||
|
$_['text_log_del_err'] = '[ОШИБКА УДАЛЕНИЯ]';
|
||||||
|
$_['text_log_quarantine'] = '[В КАРАНТИН]';
|
||||||
|
$_['text_log_q_err'] = '[ОШИБКА ПЕРЕНОСА]';
|
||||||
|
$_['text_log_restored'] = '[ВОССТАНОВЛЕНО]';
|
||||||
|
$_['text_log_rest_err'] = '[ОШИБКА ВОССТАНОВЛЕНИЯ]';
|
||||||
|
$_['text_used_in'] = 'в: ';
|
||||||
|
$_['text_analyzing'] = 'Анализ базы данных...';
|
||||||
|
$_['text_clean_empty'] = 'Чисто! Мусора нет.';
|
||||||
|
$_['text_processing'] = 'Обработка...';
|
||||||
|
$_['text_restoring'] = 'Восстановление файлов...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (И все вложенные)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Только корень)';
|
||||||
|
$_['text_q_empty_err'] = 'Папка карантина пуста или не существует.';
|
||||||
|
$_['error_token'] = 'Неверный токен. Перезагрузите страницу.';
|
||||||
|
$_['text_apply_title'] = 'Оптимизация завершена в папку: ';
|
||||||
|
$_['text_apply_info'] = 'Проверьте результат. Если всё устраивает — примените изменения к основному каталогу.';
|
||||||
|
$_['entry_apply_backup'] = 'Перенести с созданием бэкапа (в Карантин)';
|
||||||
|
$_['entry_apply_replace'] = 'Заменить оригиналы безвозвратно';
|
||||||
|
$_['button_apply_main'] = 'ПРИМЕНИТЬ К ОСНОВНОМУ КАТАЛОГУ';
|
||||||
|
$_['button_delete_copies'] = 'Удалить временные копии (отмена)';
|
||||||
|
$_['button_full_cleanup'] = 'Полная очистка всех временных папок и бэкапов';
|
||||||
|
$_['text_apply_success'] = '✅ Файлы успешно перенесены. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Оригиналы сохранены в папку: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Оригиналы удалены.';
|
||||||
|
$_['text_error_source'] = 'Ошибка: Папка с оптимизированными файлами не найдена.';
|
||||||
|
$_['confirm_full_cleanup'] = 'ВНИМАНИЕ! Это действие безвозвратно удалит ВСЕ папки бэкапов (backup_*) и временные папки с копиями. Продолжить?';
|
||||||
|
$_['text_tools_desc'] = 'Глобальные сканеры и инструменты для работы с файловой системой и БД изображений.';
|
||||||
|
$_['text_tool_select'] = 'Выберите инструмент:';
|
||||||
|
$_['text_group_scanners'] = 'Сканеры и Анализ';
|
||||||
|
$_['text_group_generators'] = 'Генераторы и Обработка';
|
||||||
|
$_['text_group_experimental'] = 'Экспериментальные функции (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Внимание: Экспериментальные функции затрагивают системные файлы или базу данных. Настоятельно рекомендуется сделать бэкап!';
|
||||||
|
$_['text_tool_broken_db'] = 'Сканер битых/пустых фото БД';
|
||||||
|
$_['text_tool_watermark'] = 'Динамический Вотермарк (Только на кэш)';
|
||||||
|
$_['text_tool_duplicates'] = 'Поиск дубликатов файлов (по MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Конвертер тяжелых PNG в JPG (сканер файлов без прозрачности)';
|
||||||
|
$_['text_tool_placeholders'] = 'Генератор заглушек (Вместо No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Очистка EXIF-данных (Геотеги/Мета)';
|
||||||
|
$_['text_tool_small_photos'] = 'Сканер слишком мелких фото';
|
||||||
|
$_['text_tool_html_broken'] = 'Поиск битых картинок в HTML (Описаниях)';
|
||||||
|
$_['text_tool_empty_folders'] = 'Очистка пустых папок (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Умная очистка кэша изображений';
|
||||||
|
$_['text_tool_folder_tree'] = 'Дерево папок (Статистика веса)';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (Без БД)';
|
||||||
|
$_['text_tool_translit'] = 'Транслитерация названий файлов';
|
||||||
|
$_['text_tool_cache_restore'] = 'Воскрешение оригиналов из Кэша';
|
||||||
|
$_['entry_wm_type'] = 'Тип вотермарка';
|
||||||
|
$_['text_wm_image'] = 'Изображение (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Текст';
|
||||||
|
$_['entry_wm_image_path'] = 'Путь к изображению';
|
||||||
|
$_['entry_wm_text_val'] = 'Текст вотермарка';
|
||||||
|
$_['entry_wm_position'] = 'Позиция (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Прозрачность (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Только для Категорий';
|
||||||
|
$_['entry_wm_brand'] = 'Только для Брендов';
|
||||||
|
$_['entry_min_width'] = 'Минимальная ширина (px)';
|
||||||
|
$_['entry_min_height'] = 'Минимальная высота (px)';
|
||||||
|
$_['entry_ph_product'] = 'Заглушка для Товаров';
|
||||||
|
$_['entry_ph_category'] = 'Заглушка для Категорий';
|
||||||
|
$_['entry_ph_brand'] = 'Заглушка для Брендов';
|
||||||
|
$_['button_run_tool'] = 'Запустить инструмент';
|
||||||
|
$_['button_scan_tool'] = 'Сканировать';
|
||||||
|
$_['button_fix_tool'] = 'Исправить найденное';
|
||||||
|
$_['text_th_path'] = 'Файл / Путь';
|
||||||
|
$_['text_th_size'] = 'Размер';
|
||||||
|
$_['text_th_action'] = 'Действие';
|
||||||
|
$_['text_th_status'] = 'Статус';
|
||||||
|
$_['text_th_problem'] = 'Проблема / Значение';
|
||||||
|
$_['text_cron_title'] = 'Настройка Cron';
|
||||||
|
$_['text_cron_desc'] = 'Добавьте команду в Cron хостинга для автоматического фонового сжатия.';
|
||||||
|
$_['entry_cron_new_only'] = 'Обрабатывать только новые (необработанные) файлы';
|
||||||
|
$_['entry_cron_folders'] = 'Папки для сканирования (оставьте пустым для всего каталога)';
|
||||||
|
$_['text_promo_title'] = 'Другие модули серии [NAT]';
|
||||||
|
$_['text_promo_desc'] = 'Модули серии [NAT] — это инструменты для глубокой оптимизации OpenCart. Мы фокусируемся на автоматизации рутины, ускорении базы данных и очистке серверов от мусора. Все модули имеют открытый исходный код и единую логику управления.';
|
||||||
|
$_['text_support'] = 'Техническая поддержка и пожелания:';
|
||||||
|
$_['button_more'] = 'Смотреть все модули на OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Скрытие пустых категорий и брендов + SEO сортировка товаров (в наличии сверху) + Очистка мусора БД.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Новинки по датам';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Автоматическая SEO-страница новинок. Отображает новинки строго по реальной дате добавления. Слайдеры, сетка и умный сайдбар архива дат.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Текущий модуль.</strong> Сжатие и ресайз ОРИГИНАЛОВ фото (image/catalog) в WebP + Очистка хостинга от неиспользуемых изображений.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Поиск и чистка внешних ссылок и HTTP-картинок в БД и шаблонах (для переезда на HTTPS).';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Создание опросов и голосований с визуальным конструктором дизайна, глубокой статистикой и защитой от накруток.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Интеллектуальная очистка мусора в базе данных, управление системными логами и добавление критически важных индексов для ускорения.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Быстрый импорт/экспорт цен и остатков из файлов xls/xlsx/csv/xml/json или по ссылке. Маппинг полей, работа по Cron, текстовые функции.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Удобные инструменты администратора: вход под клиентом, кастомизация админки, управление правами пользователей и скрытие ненужных пунктов меню.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Автоматическое отслеживание UTM-меток при оформлении заказа. Сохраняет источник перехода, кампанию, ключевые слова и выводит эти данные в заказе для аналитики рекламы.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Верхний рекламный инфо-баннер, информационная полоса над шапкой, бегущая строка и топ-баннер';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Создание интерактивных промо-панелей и слайд-шоу в шапке сайта с конструктором блоков, таймерами, формой подписки и гибким таргетингом.';
|
||||||
|
$_['text_tool_no_image'] = 'Товары без главного фото (пусто в БД)';
|
||||||
|
$_['text_problem_main_broken'] = 'Битое главное фото (файл удален)';
|
||||||
|
$_['text_problem_add_broken'] = 'Битое доп. фото (файл удален)';
|
||||||
|
$_['text_problem_no_image'] = 'Отсутствует главное фото';
|
||||||
|
$_['text_err_dir'] = 'Ошибка: Директория не найдена или неверный путь.';
|
||||||
|
$_['text_err_no_queue'] = 'Ошибка: Очередь не найдена. Сначала запустите сканирование.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Удалена пустая папка: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'Пустых папок в image/catalog/ не найдено.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Всего удалено пустых папок: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Кэш изображений успешно очищен.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Вотермарки работают в SMART-режиме: оригиналы не трогаются, логотип накладывается только при генерации кэша.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Сканирование image/catalog/ на дубликаты (по хешу MD5)... Это может занять время.';
|
||||||
|
$_['text_log_found_dupe'] = 'Найден дубликат: ';
|
||||||
|
$_['text_log_no_dupes'] = 'Дубликаты не найдены.';
|
||||||
|
$_['text_log_total_dupes'] = 'Найдено дубликатов: %s. Потенциальная экономия: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Нажмите "Исправить найденное" для перелинковки БД на оригинал и удаления дублей.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Исправлен и удален дубликат: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Успешно исправлено дубликатов: %s. Ссылки в БД обновлены.';
|
||||||
|
$_['text_log_scan_png'] = 'Поиск тяжелых PNG файлов без прозрачности...';
|
||||||
|
$_['text_log_no_png'] = 'Подходящих PNG файлов не найдено.';
|
||||||
|
$_['text_log_total_png'] = 'Найдено PNG без прозрачности: %s. Нажмите "Исправить" для конвертации в JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Сконвертировано в JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Успешно сконвертировано %s файлов. Освобождено: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Обновлено товаров заглушкой: ';
|
||||||
|
$_['text_log_ph_cat'] = 'Обновлено категорий заглушкой: ';
|
||||||
|
$_['text_log_ph_brand'] = 'Обновлено брендов заглушкой: ';
|
||||||
|
$_['text_log_ph_empty'] = 'Заглушки не выбраны в настройках.';
|
||||||
|
$_['text_log_scan_exif'] = 'Сканирование EXIF-данных (мета-теги камер)...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Очищен EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'Файлов с EXIF для очистки не найдено.';
|
||||||
|
$_['text_log_success_exif'] = 'Очищен EXIF у %s файлов. Освобождено: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Поиск фото размером менее %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Слишком маленькое (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'Мелких фото не найдено.';
|
||||||
|
$_['text_log_total_small'] = 'Найдено мелких фото: %s. Пожалуйста, замените их на качественные.';
|
||||||
|
$_['text_log_html_broken'] = 'Битый IMG в HTML таблицы %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'Битых картинок в HTML-описаниях не найдено.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Найдено битых ссылок в HTML: %s. Исправьте их вручную в редакторе.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Поиск потерянных файлов в image/cache/...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Удален потерянный кэш: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'Потерянного кэша не найдено.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Удалено файлов потерянного кэша: %s. Освобождено: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Поиск файлов с кириллицей или пробелами...';
|
||||||
|
$_['text_log_no_bad_names'] = 'Файлов с некорректными именами не найдено.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Найдено файлов с кириллицей/пробелами: %s. Нажмите "Исправить" для транслитерации и обновления БД.';
|
||||||
|
$_['text_log_renamed'] = 'Переименовано: ';
|
||||||
|
$_['text_log_success_translit'] = 'Успешно транслитерировано файлов: %s. БД обновлена.';
|
||||||
|
$_['text_log_scan_restore'] = 'Сканирование image/cache/ для восстановления утерянных оригиналов...';
|
||||||
|
$_['text_log_restored_cache'] = 'Восстановлено из кэша: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'Отсутствующих оригиналов в кэше не найдено.';
|
||||||
|
$_['text_log_success_restore'] = 'Успешно восстановлено оригиналов из кэша: %s.';
|
||||||
|
$_['error_invalid_folder'] = 'Ошибка: Недопустимое имя папки!';
|
||||||
|
$_['text_formats_title'] = 'Современные форматы (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Настройки интеграции современных форматов в файловую систему и шаблоны OpenCart.';
|
||||||
|
$_['entry_support_svg'] = 'Поддержка загрузки SVG';
|
||||||
|
$_['help_support_svg'] = 'Разрешает загрузку векторных SVG-изображений через стандартный Filemanager и их безопасный вывод на витрине без искажений размеров.';
|
||||||
|
$_['entry_support_webp'] = 'Поддержка загрузки WebP';
|
||||||
|
$_['help_support_webp'] = 'Позволяет вручную загружать, подвязывать и выводить на сайте готовые файлы .webp через Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP в TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Добавляет поддержку загрузки, предпросмотра и эскизов WebP в TrueFileManager от sitecreator (если он установлен).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Сверхбыстрая подмена: автоматически меняет расширения .jpg/.png на .webp прямо в HTML-выводе витрины и добавляет атрибут loading="lazy". Не трогает базу данных!';
|
||||||
|
$_['help_tool_broken_db'] = 'Ищет в базе данных картинки, физические файлы которых были удалены с сервера. Позволяет очистить битые ссылки.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Рекурсивно сканирует директорию image/catalog/ и безопасно удаляет все папки, в которых нет ни одного файла.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Строит дерево каталогов и показывает реальный вес каждой папки для поиска "пожирателей" места.';
|
||||||
|
$_['help_tool_html_broken'] = 'Сканирует HTML-описания товаров и категорий на наличие тегов <img> с несуществующими картинками.';
|
||||||
|
$_['help_tool_small_photos'] = 'Находит картинки с разрешением ниже заданного (например, меньше 300x300 px). Добавлены ссылки на поиск качественных аналогов в Google Images и Яндекс Картинках.';
|
||||||
|
$_['help_tool_duplicates'] = 'Ищет полные дубликаты файлов по MD5 хешу (разные имена, но одинаковая картинка). Позволяет склеить их в один файл и обновить БД.';
|
||||||
|
$_['help_tool_watermark'] = 'Наложение водяных знаков в SMART-режиме (только на кэш витрины, оригиналы не изменяются).';
|
||||||
|
$_['help_tool_placeholders'] = 'Массово устанавливает выбранную картинку-заглушку для товаров и категорий, у которых вообще нет фото.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Инструмент сканирует папку изображений и находит тяжелые PNG-файлы, которые не имеют прозрачного фона (альфа-канала). Поскольку прозрачность им не нужна, их можно безопасно конвертировать в формат JPG. Это значительно облегчает файлы (в среднем на 70-80%) и ускоряет загрузку сайта, а все ссылки в базе данных автоматически обновляются.';
|
||||||
|
$_['help_tool_exif'] = 'Сканирует JPEG-файлы и удаляет из них скрытые EXIF-данные (геотеги, данные о камере), уменьшая вес на 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Ищет в папке image/cache/ кэшированные файлы, оригиналы которых уже удалены из image/catalog/, и стирает этот мусор.';
|
||||||
|
$_['help_tool_translit'] = 'Находит файлы с кириллицей или пробелами в названиях, переименовывает их транслитом (tovar_1.jpg) и обновляет пути в БД.';
|
||||||
|
$_['help_tool_cache_restore'] = 'Если вы случайно удалили оригинал из catalog, инструмент попытается найти его копию в cache и восстановить обратно.';
|
||||||
|
$_['entry_quarantine'] = 'Переместить оригиналы в Карантин (вместо удаления)';
|
||||||
|
$_['entry_backup_db'] = 'Создать бэкап изменяемых таблиц в SQL перед запуском';
|
||||||
|
$_['button_fix_selected'] = 'ИСПРАВИТЬ ВЫБРАННЫЕ';
|
||||||
|
$_['button_export_log'] = 'Экспортировать в CSV';
|
||||||
|
$_['text_th_before'] = 'Состояние ДО';
|
||||||
|
$_['text_th_after'] = 'Ожидаемый результат ПОСЛЕ';
|
||||||
|
$_['text_th_select'] = 'Действие';
|
||||||
|
$_['text_th_preview'] = 'Предпросмотр';
|
||||||
|
$_['text_th_name'] = 'Объект / Файл';
|
||||||
|
$_['text_global_action'] = 'Групповое действие для выбранных:';
|
||||||
|
$_['text_action_clear'] = 'Очистить ссылку в БД';
|
||||||
|
$_['text_action_disable'] = 'Отключить (Статус = 0)';
|
||||||
|
$_['text_action_stock'] = 'Установить "Нет в наличии"';
|
||||||
|
$_['text_action_placeholder'] = 'Установить заглушку';
|
||||||
|
$_['text_action_ignore'] = 'Ничего не делать';
|
||||||
|
$_['text_action_merge'] = 'Склеить (удалить дубликат, оставить оригинал)';
|
||||||
|
$_['text_action_convert'] = 'Конвертировать в JPG';
|
||||||
|
$_['text_action_rename'] = 'Переименовать транслитом';
|
||||||
|
$_['text_action_delete'] = 'Удалить';
|
||||||
|
$_['text_action_restore'] = 'Восстановить';
|
||||||
|
$_['text_db_backup_created'] = 'Создан бэкап БД: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Перенесено в карантин: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Успешно выполнено';
|
||||||
|
$_['text_fix_status_error'] = 'Ошибка: %s';
|
||||||
|
$_['text_log_exported'] = 'Лог успешно экспортирован!';
|
||||||
|
$_['text_select_action'] = '-- Выберите действие --';
|
||||||
|
$_['error_empty_selection'] = 'Ошибка: Не выбрано ни одной строки для исправления!';
|
||||||
|
$_['text_link_admin'] = 'Админка';
|
||||||
|
$_['text_link_catalog'] = 'Витрина';
|
||||||
|
$_['entry_threads'] = 'Потоков обработки';
|
||||||
|
$_['text_threads_optimal'] = 'оптимально';
|
||||||
|
$_['button_download_zip'] = 'Скачать ZIP-архив и удалить папку';
|
||||||
|
$_['confirm_download_and_delete'] = 'Архив успешно скачался? Можно удалить временную папку копий на сервере?';
|
||||||
|
$_['button_clear_backups'] = 'Удалить бэкапы БД';
|
||||||
|
$_['button_clear_backups_short'] = 'Очистить бэкапы';
|
||||||
|
$_['confirm_clear_backups'] = 'Вы уверены, что хотите полностью удалить все SQL-бэкапы таблиц?';
|
||||||
|
$_['text_compare_title'] = 'Сравнение изображений ДО / ПОСЛЕ';
|
||||||
|
$_['text_compare_before'] = 'ДО';
|
||||||
|
$_['text_compare_after'] = 'ПОСЛЕ';
|
||||||
|
$_['text_backups_cleared'] = 'Все SQL-бэкапы таблиц успешно удалены!';
|
||||||
|
$_['error_no_backups'] = 'Бэкапы не найдены или папка пуста.';
|
||||||
|
$_['button_restore'] = 'Вернуть';
|
||||||
|
$_['button_empty'] = 'Очистить';
|
||||||
|
$_['entry_wm_targets'] = 'Применять водяной знак к:';
|
||||||
|
$_['text_wm_target_product'] = 'Фото товаров';
|
||||||
|
$_['text_wm_target_category'] = 'Фото категорий';
|
||||||
|
$_['text_wm_target_brand'] = 'Логотипы брендов';
|
||||||
|
$_['text_wm_target_banner'] = 'Баннеры и слайды';
|
||||||
|
$_['text_wm_target_blog'] = 'Статьи / Блог';
|
||||||
|
$_['error_no_scan_data'] = 'Ошибка: Данные сканирования не найдены. Сначала запустите сканирование!';
|
||||||
|
$_['error_row_not_found'] = 'Ошибка: Объект не найден в результатах сканирования!';
|
||||||
|
$_['error_file_not_found'] = 'Ошибка: Файл не найден на сервере!';
|
||||||
|
$_['text_folder_rules_title'] = 'Индивидуальные правила сжатия для папок';
|
||||||
|
$_['text_folder_rules_desc'] = 'Вы можете переопределить качество и максимальные размеры для конкретных папок. Если оставить поле пустым, будет использоваться глобальная настройка. Путь указывается относительно папки image (например, catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Путь к папке';
|
||||||
|
$_['entry_jpg_quality'] = 'Качество JPG';
|
||||||
|
$_['entry_webp_quality'] = 'Качество WebP';
|
||||||
|
$_['button_add_rule'] = 'Добавить правило';
|
||||||
|
$_['button_remove'] = 'Удалить';
|
||||||
|
$_['entry_wm_angle'] = 'Угол наклона водяного знака (градусы)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Скругление углов водяного знака (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Масштабирование водяного знака';
|
||||||
|
$_['entry_wm_size_percent'] = 'Размер водяного знака (% от ширины изображения)';
|
||||||
|
$_['entry_wm_text_color'] = 'Цвет текста водяного знака';
|
||||||
|
$_['entry_wm_text_font'] = 'Шрифт водяного знака (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Режим фильтрации Категорий/Брендов';
|
||||||
|
$_['text_wm_filter_include'] = 'Только для выбранных';
|
||||||
|
$_['text_wm_filter_exclude'] = 'Для всех, кроме выбранных';
|
||||||
|
$_['text_wm_size_original'] = 'Оригинальный размер';
|
||||||
|
$_['text_wm_size_percent'] = 'Пропорционально ширине изображения (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Предпросмотр водяного знака';
|
||||||
|
$_['text_wm_preview_title'] = 'Предпросмотр водяного знака';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Исключить папки из сканирования';
|
||||||
|
$_['text_cron_entities'] = 'Какие типы фото обрабатывать?';
|
||||||
|
$_['text_cron_quality_override'] = 'Переопределить параметры сжатия для Крона';
|
||||||
|
$_['text_cron_recommendations'] = 'Рекомендации по расписанию запуска Cron';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>Для небольших сайтов (до 5000 товаров)</b> рекомендуется запускать Крон каждый час (<code>0 * * * *</code>).</li><li><b>Для средних сайтов (5000-20000 товаров)</b> рекомендуется запускать раз в 3-6 часов (<code>0 */6 * * *</code>).</li><li><b>Для крупных сайтов (более 20000 товаров)</b> — один раз в сутки ночью (например, в 2:00: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Сканер битых/пустых файлов на сервере';
|
||||||
|
$_['help_tool_broken_files'] = 'Сканирует физические файлы в папке image/catalog/ на наличие поврежденных изображений или пустых файлов размером 0 байт.';
|
||||||
|
$_['text_grid_broken_file'] = 'Битый или пустой файл на сервере';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Удаление или замена заглушкой';
|
||||||
|
$_['text_description_broken_files'] = 'Результаты сканирования физических файлов в папке image/catalog/. Здесь отображаются битые (поврежденные) изображения или файлы размером 0 байт.';
|
||||||
|
$_['text_action_strip'] = 'Очистить EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Установить заглушку';
|
||||||
|
$_['text_th_entity'] = 'Объект / Связанные ссылки';
|
||||||
|
$_['text_cron_entity_product'] = 'Товары';
|
||||||
|
$_['text_cron_entity_category'] = 'Категории';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Производители / Бренды';
|
||||||
|
$_['text_cron_entity_banner'] = 'Баннеры';
|
||||||
|
$_['text_cron_entity_blog'] = 'Блог / Статьи';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'Качество JPG (Крон)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'Качество WebP (Крон)';
|
||||||
|
$_['entry_cron_max_width'] = 'Макс. ширина px (Крон)';
|
||||||
|
$_['entry_cron_max_height'] = 'Макс. высота px (Крон)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Папки для сканирования';
|
||||||
|
$_['text_no_fonts'] = 'TTF-шрифты не найдены в system/library/font/';
|
||||||
|
$_['button_apply'] = 'Применить';
|
||||||
|
$_['text_backup_will_be_created'] = 'Резервная копия базы данных будет автоматически создана перед выполнением этого действия.';
|
||||||
|
$_['text_bulk_action'] = 'Групповое действие для выбранных';
|
||||||
|
$_['text_grid_results'] = 'Результаты и детали сканирования';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Оригинальные файлы будут перемещены в карантин (image/catalog_trash/) перед изменением.';
|
||||||
|
$_['text_success_apply'] = 'Изменения успешно применены!';
|
||||||
|
$_['text_description_broken'] = 'Результаты сканирования базы данных на наличие битых ссылок (записи в таблицах с несуществующими файлами).';
|
||||||
|
$_['text_description_cache_restore'] = 'Результаты поиска файлов в кэше, оригиналы которых отсутствуют в каталоге.';
|
||||||
|
$_['text_description_duplicates'] = 'Результаты поиска полных дубликатов изображений по MD5-хешу.';
|
||||||
|
$_['text_description_empty_folders'] = 'Список пустых папок в директории image/catalog/ для удаления.';
|
||||||
|
$_['text_description_exif'] = 'Результаты поиска изображений с метаданными EXIF, которые можно очистить.';
|
||||||
|
$_['text_description_folder_tree'] = 'Статистика размера папок в каталоге image/catalog/.';
|
||||||
|
$_['text_description_html_broken'] = 'Результаты поиска несуществующих файлов картинок, на которые ссылаются описания в HTML.';
|
||||||
|
$_['text_description_placeholders'] = 'Результаты поиска сущностей без изображений для заполнения заглушками.';
|
||||||
|
$_['text_description_png_jpg'] = 'Результаты поиска тяжелых изображений PNG без прозрачности, которые можно пережать в JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'Список изображений, размеры которых меньше минимально заданных (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Результаты поиска файлов кэша, оригиналы которых уже были удалены.';
|
||||||
|
$_['text_description_translit'] = 'Результаты поиска файлов с кириллицей или пробелами в названиях.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Очистить ссылку или установить заглушку';
|
||||||
|
$_['text_grid_dupe_after'] = 'Оригинал (остаётся)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Дубликат (будет удалён)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Пустая папка';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Удалить пустую папку';
|
||||||
|
$_['text_grid_exif_after'] = 'Очистить метаданные';
|
||||||
|
$_['text_grid_exif_before'] = 'Содержит EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Исправить ссылку вручную';
|
||||||
|
$_['text_grid_missing_file'] = 'Файл отсутствует';
|
||||||
|
$_['text_grid_missing_html'] = 'Картинка не найдена в описании';
|
||||||
|
$_['text_grid_missing_orig'] = 'Оригинал отсутствует';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Восстановить оригинал из кэша';
|
||||||
|
$_['text_grid_no_image'] = 'Нет изображения (пусто)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Осиротевший кэш';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Удалить ненужный кэш';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Установить заглушку бренда';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Установить заглушку категории';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Установить заглушку товара';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Рекомендуется заменить на более крупное';
|
||||||
|
$_['text_grid_too_small'] = 'Слишком маленькое';
|
||||||
|
$_['text_no_relations'] = 'Нет связей в БД';
|
||||||
|
$_['text_action_archive'] = 'Архивировать (в ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Архивировать + Удалить';
|
||||||
|
$_['text_tool_sanitizer'] = 'Санитайзер имен файлов';
|
||||||
|
$_['help_tool_sanitizer'] = 'Массовая очистка имен файлов и папок от кириллицы, пробелов и спецсимволов, приведение расширений к единому виду.';
|
||||||
|
$_['text_description_sanitizer'] = 'Результаты анализа имен файлов для санитаризации по выбранным правилам.';
|
||||||
|
$_['entry_sz_translit'] = 'Транслитерация (кириллица в латиницу)';
|
||||||
|
$_['entry_sz_spaces'] = 'Заменять пробелы на символ "_"';
|
||||||
|
$_['entry_sz_special'] = 'Удалять специальные символы';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Имя файла в нижний регистр';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Расширение в нижний регистр';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Нормализовать расширения (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Режим фильтрации папок';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Сканировать все, кроме выбранных (Исключения)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Сканировать только выбранные папки';
|
||||||
|
$_['button_select_all'] = 'Выбрать все';
|
||||||
|
$_['button_deselect_all'] = 'Снять все';
|
||||||
|
$_['entry_wm_upload_font'] = 'Загрузить свой шрифт TTF';
|
||||||
|
$_['button_upload_font'] = 'Загрузить шрифт';
|
||||||
|
$_['text_success_font_upload'] = 'Шрифт успешно загружен!';
|
||||||
|
$_['error_font_upload'] = 'Ошибка загрузки шрифта! Разрешены только файлы .ttf весом до 5 МБ.';
|
||||||
|
$_['entry_lazy_load'] = 'Прогрессивная ленивая загрузка';
|
||||||
|
$_['help_lazy_load'] = 'Включает премиальную прогрессивную отложенную загрузку на витрине. Изображения заменяются микро-превью и плавно размываются (blur-up), подгружаясь по мере прокрутки.';
|
||||||
|
$_['confirm_replace_mode'] = 'ВНИМАНИЕ! Вы выбрали режим «Заменять оригиналы». Все изображения будут перезаписаны прямо на сервере. Настоятельно рекомендуем сделать резервную копию. Вы уверены, что хотите продолжить?';
|
||||||
|
$_['text_compare'] = 'Сравнить';
|
||||||
|
$_['text_home'] = 'Главная';
|
||||||
|
$_['text_tech_gd'] = 'Библиотека GD';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'Поддержка WebP';
|
||||||
|
$_['text_position_1'] = '1 - Слева вверху';
|
||||||
|
$_['text_position_2'] = '2 - По центру вверху';
|
||||||
|
$_['text_position_3'] = '3 - Справа вверху';
|
||||||
|
$_['text_position_4'] = '4 - Слева посередине';
|
||||||
|
$_['text_position_5'] = '5 - По центру';
|
||||||
|
$_['text_position_6'] = '6 - Справа посередине';
|
||||||
|
$_['text_position_7'] = '7 - Слева внизу';
|
||||||
|
$_['text_position_8'] = '8 - По центру внизу';
|
||||||
|
$_['text_position_9'] = '9 - Справа внизу';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Уровень лога';
|
||||||
|
$_['text_log_changed_only'] = 'Только изменённые';
|
||||||
|
$_['text_log_all'] = 'Все файлы (подробно)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Пропущен (нет улучшения)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Сбросить кэш оптимизации (полный пересканирование)';
|
||||||
|
$_['confirm_reset_cache'] = 'Сбросить кэш оптимизации? Следующее сканирование проверит ВСЕ файлы заново.';
|
||||||
|
$_['text_cache_cleared'] = 'Кэш оптимизации очищен. Следующее сканирование проверит все файлы.';
|
||||||
|
$_['error_wm_preview'] = 'Ошибка генерации превью водяного знака.';
|
||||||
|
$_['error_wm_too_large'] = 'Изображение водяного знака слишком большое (%dx%d px)! Максимально допустимый размер: %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'ВНИМАНИЕ: Некоторые файлы не удалось заменить (заблокированы или нет прав):';
|
||||||
|
$_['text_action_compress'] = 'Сжать изображения';
|
||||||
|
$_['text_estimated'] = 'Прогноз';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Мин. сжатие (байт)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Мин. сжатие (%)';
|
||||||
|
$_['text_optimized'] = 'Оптимизировано';
|
||||||
|
$_['entry_broken_fallback'] = 'Заглушка для битых фото';
|
||||||
|
$_['help_broken_fallback'] = 'Автоматически заменяет отсутствующие или поврежденные изображения каталога на витрине на изображение-заглушку.';
|
||||||
|
$_['entry_fallback_image'] = 'Изображение-заглушка';
|
||||||
|
$_['help_fallback_image'] = 'Выберите изображение, которое будет показано, если оригинальный файл картинки товара или категории отсутствует. По умолчанию используется no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'Конвертер CMYK в RGB';
|
||||||
|
$_['help_tool_cmyk'] = 'Конвертация JPEG изображений из цветового профиля CMYK в sRGB для корректного отображения в браузерах Safari и на устройствах iOS.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Поиск тяжелых файлов';
|
||||||
|
$_['help_tool_heavy_files'] = 'Поиск самых тяжелых оригинальных изображений в каталоге с возможностью их быстрого сжатия прямо здесь.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Макс. количество найденных файлов (всего)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Конвертировать в RGB';
|
||||||
|
$_['text_description_cmyk'] = 'Найдены файлы в цветовом пространстве CMYK. Конвертация в sRGB гарантирует корректное отображение на всех платформах, включая iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'Список из %d самых тяжелых файлов в каталоге изображений. Вы можете оптимизировать их прямо здесь.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Статус: Будут сканироваться все папки (исключения не выбраны).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Статус: Будут сканироваться все папки, КРОМЕ %d отмеченных.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Статус: Будут сканироваться только %d отмеченных папок.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'ВНИМАНИЕ: Ни одна папка не выбрана! Крон ничего не будет сканировать.';
|
||||||
|
$_['text_action_apply_changes'] = 'Применить указанные изменения';
|
||||||
|
$_['entry_wm_status'] = 'Статус водяного знака';
|
||||||
|
$_['text_disabled'] = 'Отключено';
|
||||||
|
$_['entry_rule_name'] = 'Название правила';
|
||||||
|
$_['entry_rule_status'] = 'Статус правила';
|
||||||
|
$_['entry_action'] = 'Действие';
|
||||||
|
$_['entry_ph_status'] = 'Статус заглушек';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Макс. файлов для поиска (лимит)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Минимальный размер';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Макс. файлов для поиска (лимит)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Минимальный размер';
|
||||||
|
$_['text_wm_rule_title'] = 'Правило водяного знака';
|
||||||
|
$_['button_close'] = 'Закрыть';
|
||||||
|
$_['text_ph_rule_title'] = 'Правило заглушки';
|
||||||
|
$_['entry_ph_image'] = 'Изображение заглушки';
|
||||||
|
$_['button_clear_cache_wm'] = 'Очистить кэш (применить водяной знак)';
|
||||||
|
$_['text_active'] = 'Активна';
|
||||||
|
$_['text_error'] = 'Не активна';
|
||||||
|
$_['text_th_dupe_delete'] = 'Файл для удаления (Дубликат)';
|
||||||
|
$_['text_th_dupe_keep'] = 'Файл для сохранения (Оригинал)';
|
||||||
|
$_['text_dupe_original_label'] = 'Оригинал';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Дубликат';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Использование диска';
|
||||||
|
$_['text_cumulative_stats'] = 'Статистика оптимизации';
|
||||||
|
$_['text_disk_catalog'] = 'Каталог изображений';
|
||||||
|
$_['text_disk_cache'] = 'Кэш OC';
|
||||||
|
$_['text_disk_trash'] = 'Корзина модуля';
|
||||||
|
$_['text_disk_other'] = 'Прочее';
|
||||||
|
$_['text_disk_free'] = 'Свободно';
|
||||||
|
$_['text_total_files_opt'] = 'Файлов обработано';
|
||||||
|
$_['text_total_saved'] = 'Всего сэкономлено';
|
||||||
|
$_['text_avg_saving_pct'] = 'Средняя экономия';
|
||||||
|
$_['text_stats_hint'] = 'Статистика накапливается по всем сессиям (хранится локально в браузере)';
|
||||||
|
$_['text_reset_stats'] = 'Сбросить статистику';
|
||||||
|
$_['text_reset_stats_confirm']= 'Сбросить накопленную статистику оптимизации?';
|
||||||
|
$_['text_export_import'] = 'Экспорт / Импорт настроек';
|
||||||
|
$_['button_export_settings'] = 'Экспорт настроек (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Импортировать настройки';
|
||||||
|
$_['help_export_settings'] = 'Скачать все настройки модуля в JSON-файл';
|
||||||
|
$_['help_import_settings'] = 'Загрузить ранее экспортированный JSON для восстановления настроек';
|
||||||
|
$_['text_import_success'] = 'Настройки успешно импортированы. Перезагрузите страницу для применения.';
|
||||||
|
$_['error_import_file'] = 'Файл не загружен или ошибка загрузки';
|
||||||
|
$_['error_import_invalid'] = 'Некорректный файл настроек (неверный модуль или формат)';
|
||||||
|
$_['text_click_to_load'] = 'Нажмите ↑ для загрузки данных диска';
|
||||||
|
$_['tab_formats'] = 'Форматы изображений';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Лимиты диска хостинга (вручную)';
|
||||||
|
$_['text_disk_quota_desc'] = 'Если график дискового пространства показывает неверные данные (физический диск сервера вместо лимита вашего хостинга), вы можете указать лимиты вашего тарифа вручную. Укажите 0, чтобы использовать автоопределение.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Выделено диска на хостинге (ГБ)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
|
||||||
|
$_['entry_auto_disk'] = 'Автообновление при входе';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Название задачи';
|
||||||
|
$_['entry_cron_task_summary'] = 'Параметры сжатия';
|
||||||
|
$_['button_add_cron_task'] = 'Добавить задачу';
|
||||||
|
$_['button_copy_cron_url'] = 'Копировать URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'Требуется лицензионный ключ';
|
||||||
|
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
|
||||||
|
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
|
||||||
|
$_['button_activate'] = 'Активировать модуль';
|
||||||
|
$_['text_support'] = 'Техническая поддержка';
|
||||||
|
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
|
||||||
|
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
|
||||||
|
$_['help_wm_max_height'] = 'Если исходное изображение водяного знака превышает эту высоту, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
|
||||||
|
$_['entry_disk_status'] = 'Включить сканирование диска и график';
|
||||||
|
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
|
||||||
|
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,564 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Сжатие оригиналов и удаление мусора';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Сжатие оригиналов и удаление мусора';
|
||||||
|
$_['text_extension'] = 'Расширения';
|
||||||
|
$_['text_edit'] = 'Настройки модуля';
|
||||||
|
$_['text_success'] = 'Настройки успешно сохранены!';
|
||||||
|
$_['entry_warning'] = 'ВНИМАНИЕ: Обязательно сделайте бэкап файлов сайта и особенно папки /image/catalog перед началом работы!';
|
||||||
|
$_['text_tech'] = 'Проверка технологий сервера:';
|
||||||
|
$_['text_author'] = 'Техническая поддержка и пожелания: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Оптимизация и Ресайз';
|
||||||
|
$_['tab_cleaner'] = 'Очистка неиспользуемых изображений';
|
||||||
|
$_['tab_info'] = 'Инфо / Экосистема [NAT]';
|
||||||
|
$_['tab_settings'] = 'Настройки';
|
||||||
|
$_['tab_tools'] = 'Инструменты (pro)';
|
||||||
|
$_['tab_cron'] = 'Крон-задачи';
|
||||||
|
$_['entry_mode'] = 'Режим работы';
|
||||||
|
$_['text_copy'] = 'Создать копию (безопасно)';
|
||||||
|
$_['text_replace'] = 'Заменять оригиналы (требует бэкап!)';
|
||||||
|
$_['entry_folder'] = 'Имя папки для копии';
|
||||||
|
$_['entry_batch'] = 'Файлов за проход';
|
||||||
|
$_['entry_log_limit'] = 'Строк лога на экране';
|
||||||
|
$_['entry_max_width'] = 'Макс. ширина (px) [0 - без лимита]';
|
||||||
|
$_['entry_max_height'] = 'Макс. высота (px) [0 - без лимита]';
|
||||||
|
$_['entry_jpg'] = 'Качество JPG (0-100)';
|
||||||
|
$_['entry_png'] = 'Сжатие PNG (0-9)';
|
||||||
|
$_['entry_webp'] = 'Качество WebP (0-100)';
|
||||||
|
$_['entry_targets'] = 'Что оптимизировать?';
|
||||||
|
$_['text_all_catalog'] = '[ Весь каталог image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Файлы только в корне image/catalog';
|
||||||
|
$_['entry_threshold'] = 'Порог замены (%)';
|
||||||
|
$_['help_threshold'] = '0: заменять только если вес стал меньше. 10: разрешить увеличение веса до 10%.';
|
||||||
|
$_['entry_token'] = 'Токен активации (на корень домена)';
|
||||||
|
$_['entry_status'] = 'Статус модуля';
|
||||||
|
$_['entry_menu_position'] = 'Позиция меню [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Только в модулях';
|
||||||
|
$_['text_menu_header'] = 'В шапке (Header)';
|
||||||
|
$_['text_menu_sidebar'] = 'В боковом меню (Sidebar)';
|
||||||
|
$_['text_menu_both'] = 'Везде (Шапка + Сайдбар)';
|
||||||
|
$_['text_status_on'] = 'Включено';
|
||||||
|
$_['text_status_off'] = 'Отключено';
|
||||||
|
$_['entry_engine'] = 'Движок обработки изображений';
|
||||||
|
$_['text_engine_gd'] = 'GD (Стандарт)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (Высокое качество, сохранение профилей sRGB)';
|
||||||
|
$_['entry_license'] = 'Лицензия';
|
||||||
|
$_['text_cleaner_info'] = 'Этот инструмент ищет файлы, которые физически существуют на диске, но нигде не используются в базе данных или файлах шаблона.';
|
||||||
|
$_['entry_clean_targets'] = 'В каких папках искать мусор?';
|
||||||
|
$_['entry_extended_log'] = 'Расширенный лог (показать используемые фото и где они найдены)';
|
||||||
|
$_['entry_process_broken'] = 'Учитывать файлы с битой кодировкой (имена с символом "?")';
|
||||||
|
$_['button_scan_trash'] = 'НАЙТИ МУСОР';
|
||||||
|
$_['button_quarantine'] = 'В Карантин (Безопасно)';
|
||||||
|
$_['button_delete_trash'] = 'Удалить навсегда';
|
||||||
|
$_['button_empty_quarantine'] = 'Очистить папку Карантина';
|
||||||
|
$_['button_restore_quarantine'] = 'Вернуть из Карантина';
|
||||||
|
$_['text_scan_result'] = 'Результаты сканирования';
|
||||||
|
$_['text_trash_files'] = 'Мусорных файлов: ';
|
||||||
|
$_['text_trash_size'] = 'Потенциально освободится: ';
|
||||||
|
$_['button_start'] = 'СТАРТ ОПТИМИЗАЦИИ';
|
||||||
|
$_['button_pause'] = 'ПАУЗА';
|
||||||
|
$_['button_stop'] = 'ОСТАНОВИТЬ';
|
||||||
|
$_['button_continue'] = 'ПРОДОЛЖИТЬ';
|
||||||
|
$_['button_start_new'] = 'СТАРТ (Новый запуск)';
|
||||||
|
$_['button_download_log'] = 'Скачать лог в TXT';
|
||||||
|
$_['button_download'] = 'Скачать';
|
||||||
|
$_['button_save'] = 'Сохранить';
|
||||||
|
$_['button_cancel'] = 'Отмена';
|
||||||
|
$_['text_progress'] = 'Прогресс:';
|
||||||
|
$_['text_old_size'] = 'Было:';
|
||||||
|
$_['text_new_size'] = 'Стало:';
|
||||||
|
$_['text_savings'] = 'Экономия:';
|
||||||
|
$_['text_detailed_log'] = 'Детальный лог';
|
||||||
|
$_['text_page'] = 'Стр.';
|
||||||
|
$_['text_waiting'] = 'Ожидание запуска...';
|
||||||
|
$_['text_enabled'] = 'Включено';
|
||||||
|
$_['text_disabled_error'] = 'Выключено (ОШИБКА)';
|
||||||
|
$_['text_supported'] = 'Поддерживается';
|
||||||
|
$_['text_no'] = 'Нет';
|
||||||
|
$_['error_permission'] = 'У вас нет прав для управления модулем!';
|
||||||
|
$_['error_license'] = 'Ошибка доступа: Неверный токен активации для этого домена!';
|
||||||
|
$_['error_domain'] = ' (Домен: ';
|
||||||
|
$_['text_found'] = 'Найдено файлов: ';
|
||||||
|
$_['text_compressed'] = 'Сжат';
|
||||||
|
$_['text_original'] = 'Оригинал';
|
||||||
|
$_['text_error_process'] = 'Ошибка обработки: ';
|
||||||
|
$_['error_log_empty'] = 'Лог пуст! Сначала запустите процесс.';
|
||||||
|
$_['text_report_header'] = '=== ОТЧЕТ ===';
|
||||||
|
$_['text_total_files'] = 'Всего файлов: ';
|
||||||
|
$_['text_searching'] = 'Поиск файлов...';
|
||||||
|
$_['error_ajax'] = 'Ошибка: ';
|
||||||
|
$_['text_resumed'] = '▶ ПРОЦЕСС ВОЗОБНОВЛЕН...';
|
||||||
|
$_['text_paused'] = '⏸ ПАУЗА. Процесс приостановлен.';
|
||||||
|
$_['confirm_stop'] = 'Вы уверены, что хотите полностью прервать процесс?';
|
||||||
|
$_['confirm_delete'] = 'ВНИМАНИЕ! Файлы будут удалены с сервера навсегда без возможности восстановления! Продолжить?';
|
||||||
|
$_['confirm_empty_q'] = 'Вы уверены, что хотите полностью очистить папку карантина?';
|
||||||
|
$_['confirm_restore'] = 'Все файлы из папки карантина будут возвращены на свои места. Продолжить?';
|
||||||
|
$_['button_stopping'] = 'Остановка...';
|
||||||
|
$_['text_stopped_user'] = '🛑 ПРОЦЕСС ПРЕРВАН ПОЛЬЗОВАТЕЛЕМ!';
|
||||||
|
$_['text_increase'] = 'Увеличение';
|
||||||
|
$_['text_done'] = '✅ ГОТОВО! Процесс завершен.';
|
||||||
|
$_['error_timeout'] = 'Таймаут сервера. Автоповтор через 3 секунды...';
|
||||||
|
$_['text_building_index'] = 'Шаг 1/2: Индексация БД и шаблонов (поиск используемых фото)...';
|
||||||
|
$_['text_comparing'] = 'Шаг 2/2: Сравнение физических файлов с индексом...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Процесс переноса в image/catalog_trash/ завершен!';
|
||||||
|
$_['text_deleted_done'] = '✅ Процесс удаления мусора завершен!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Папка карантина успешно удалена с сервера!';
|
||||||
|
$_['text_restore_done'] = '✅ Файлы успешно возвращены из карантина в рабочую папку!';
|
||||||
|
$_['text_quarantine_move'] = 'В карантине: ';
|
||||||
|
$_['text_deleted_file'] = 'Удален: ';
|
||||||
|
$_['text_no_trash'] = 'В выбранных папках мусор не найден! Все файлы используются.';
|
||||||
|
$_['text_scanning_dirs'] = 'Сканируем директории: ';
|
||||||
|
$_['text_log_used'] = '[ИСПОЛЬЗУЕТСЯ]';
|
||||||
|
$_['text_log_trash'] = '[МУСОР]';
|
||||||
|
$_['text_log_deleted'] = '[УДАЛЕНО]';
|
||||||
|
$_['text_log_del_err'] = '[ОШИБКА УДАЛЕНИЯ]';
|
||||||
|
$_['text_log_quarantine'] = '[В КАРАНТИН]';
|
||||||
|
$_['text_log_q_err'] = '[ОШИБКА ПЕРЕНОСА]';
|
||||||
|
$_['text_log_restored'] = '[ВОССТАНОВЛЕНО]';
|
||||||
|
$_['text_log_rest_err'] = '[ОШИБКА ВОССТАНОВЛЕНИЯ]';
|
||||||
|
$_['text_used_in'] = 'в: ';
|
||||||
|
$_['text_analyzing'] = 'Анализ базы данных...';
|
||||||
|
$_['text_clean_empty'] = 'Чисто! Мусора нет.';
|
||||||
|
$_['text_processing'] = 'Обработка...';
|
||||||
|
$_['text_restoring'] = 'Восстановление файлов...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (И все вложенные)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Только корень)';
|
||||||
|
$_['text_q_empty_err'] = 'Папка карантина пуста или не существует.';
|
||||||
|
$_['error_token'] = 'Неверный токен. Перезагрузите страницу.';
|
||||||
|
$_['text_apply_title'] = 'Оптимизация завершена в папку: ';
|
||||||
|
$_['text_apply_info'] = 'Проверьте результат. Если всё устраивает — примените изменения к основному каталогу.';
|
||||||
|
$_['entry_apply_backup'] = 'Перенести с созданием бэкапа (в Карантин)';
|
||||||
|
$_['entry_apply_replace'] = 'Заменить оригиналы безвозвратно';
|
||||||
|
$_['button_apply_main'] = 'ПРИМЕНИТЬ К ОСНОВНОМУ КАТАЛОГУ';
|
||||||
|
$_['button_delete_copies'] = 'Удалить временные копии (отмена)';
|
||||||
|
$_['button_full_cleanup'] = 'Полная очистка всех временных папок и бэкапов';
|
||||||
|
$_['text_apply_success'] = '✅ Файлы успешно перенесены. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Оригиналы сохранены в папку: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Оригиналы удалены.';
|
||||||
|
$_['text_error_source'] = 'Ошибка: Папка с оптимизированными файлами не найдена.';
|
||||||
|
$_['confirm_full_cleanup'] = 'ВНИМАНИЕ! Это действие безвозвратно удалит ВСЕ папки бэкапов (backup_*) и временные папки с копиями. Продолжить?';
|
||||||
|
$_['text_tools_desc'] = 'Глобальные сканеры и инструменты для работы с файловой системой и БД изображений.';
|
||||||
|
$_['text_tool_select'] = 'Выберите инструмент:';
|
||||||
|
$_['text_group_scanners'] = 'Сканеры и Анализ';
|
||||||
|
$_['text_group_generators'] = 'Генераторы и Обработка';
|
||||||
|
$_['text_group_experimental'] = 'Экспериментальные функции (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Внимание: Экспериментальные функции затрагивают системные файлы или базу данных. Настоятельно рекомендуется сделать бэкап!';
|
||||||
|
$_['text_tool_broken_db'] = 'Сканер битых/пустых фото БД';
|
||||||
|
$_['text_tool_watermark'] = 'Динамический Вотермарк (Только на кэш)';
|
||||||
|
$_['text_tool_duplicates'] = 'Поиск дубликатов файлов (по MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Конвертер тяжелых PNG в JPG (сканер файлов без прозрачности)';
|
||||||
|
$_['text_tool_placeholders'] = 'Генератор заглушек (Вместо No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Очистка EXIF-данных (Геотеги/Мета)';
|
||||||
|
$_['text_tool_small_photos'] = 'Сканер слишком мелких фото';
|
||||||
|
$_['text_tool_html_broken'] = 'Поиск битых картинок в HTML (Описаниях)';
|
||||||
|
$_['text_tool_empty_folders'] = 'Очистка пустых папок (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Умная очистка кэша изображений';
|
||||||
|
$_['text_tool_folder_tree'] = 'Дерево папок (Статистика веса)';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (Без БД)';
|
||||||
|
$_['text_tool_translit'] = 'Транслитерация названий файлов';
|
||||||
|
$_['text_tool_cache_restore'] = 'Воскрешение оригиналов из Кэша';
|
||||||
|
$_['entry_wm_type'] = 'Тип вотермарка';
|
||||||
|
$_['text_wm_image'] = 'Изображение (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Текст';
|
||||||
|
$_['entry_wm_image_path'] = 'Путь к изображению';
|
||||||
|
$_['entry_wm_text_val'] = 'Текст вотермарка';
|
||||||
|
$_['entry_wm_position'] = 'Позиция (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Прозрачность (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Только для Категорий';
|
||||||
|
$_['entry_wm_brand'] = 'Только для Брендов';
|
||||||
|
$_['entry_min_width'] = 'Минимальная ширина (px)';
|
||||||
|
$_['entry_min_height'] = 'Минимальная высота (px)';
|
||||||
|
$_['entry_ph_product'] = 'Заглушка для Товаров';
|
||||||
|
$_['entry_ph_category'] = 'Заглушка для Категорий';
|
||||||
|
$_['entry_ph_brand'] = 'Заглушка для Брендов';
|
||||||
|
$_['button_run_tool'] = 'Запустить инструмент';
|
||||||
|
$_['button_scan_tool'] = 'Сканировать';
|
||||||
|
$_['button_fix_tool'] = 'Исправить найденное';
|
||||||
|
$_['text_th_path'] = 'Файл / Путь';
|
||||||
|
$_['text_th_size'] = 'Размер';
|
||||||
|
$_['text_th_action'] = 'Действие';
|
||||||
|
$_['text_th_status'] = 'Статус';
|
||||||
|
$_['text_th_problem'] = 'Проблема / Значение';
|
||||||
|
$_['text_cron_title'] = 'Настройка Cron';
|
||||||
|
$_['text_cron_desc'] = 'Добавьте команду в Cron хостинга для автоматического фонового сжатия.';
|
||||||
|
$_['entry_cron_new_only'] = 'Обрабатывать только новые (необработанные) файлы';
|
||||||
|
$_['entry_cron_folders'] = 'Папки для сканирования (оставьте пустым для всего каталога)';
|
||||||
|
$_['text_promo_title'] = 'Другие модули серии [NAT]';
|
||||||
|
$_['text_promo_desc'] = 'Модули серии [NAT] — это инструменты для глубокой оптимизации OpenCart. Мы фокусируемся на автоматизации рутины, ускорении базы данных и очистке серверов от мусора. Все модули имеют открытый исходный код и единую логику управления.';
|
||||||
|
$_['text_support'] = 'Техническая поддержка и пожелания:';
|
||||||
|
$_['button_more'] = 'Смотреть все модули на OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Скрытие пустых категорий и брендов + SEO сортировка товаров (в наличии сверху) + Очистка мусора БД.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Новинки по датам';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Автоматическая SEO-страница новинок. Отображает новинки строго по реальной дате добавления. Слайдеры, сетка и умный сайдбар архива дат.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Текущий модуль.</strong> Сжатие и ресайз ОРИГИНАЛОВ фото (image/catalog) в WebP + Очистка хостинга от неиспользуемых изображений.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Поиск и чистка внешних ссылок и HTTP-картинок в БД и шаблонах (для переезда на HTTPS).';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Создание опросов и голосований с визуальным конструктором дизайна, глубокой статистикой и защитой от накруток.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Интеллектуальная очистка мусора в базе данных, управление системными логами и добавление критически важных индексов для ускорения.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Быстрый импорт/экспорт цен и остатков из файлов xls/xlsx/csv/xml/json или по ссылке. Маппинг полей, работа по Cron, текстовые функции.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Удобные инструменты администратора: вход под клиентом, кастомизация админки, управление правами пользователей и скрытие ненужных пунктов меню.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Автоматическое отслеживание UTM-меток при оформлении заказа. Сохраняет источник перехода, кампанию, ключевые слова и выводит эти данные в заказе для аналитики рекламы.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Верхний рекламный инфо-баннер, информационная полоса над шапкой, бегущая строка и топ-баннер';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Создание интерактивных промо-панелей и слайд-шоу в шапке сайта с конструктором блоков, таймерами, формой подписки и гибким таргетингом.';
|
||||||
|
$_['text_tool_no_image'] = 'Товары без главного фото (пусто в БД)';
|
||||||
|
$_['text_problem_main_broken'] = 'Битое главное фото (файл удален)';
|
||||||
|
$_['text_problem_add_broken'] = 'Битое доп. фото (файл удален)';
|
||||||
|
$_['text_problem_no_image'] = 'Отсутствует главное фото';
|
||||||
|
$_['text_err_dir'] = 'Ошибка: Директория не найдена или неверный путь.';
|
||||||
|
$_['text_err_no_queue'] = 'Ошибка: Очередь не найдена. Сначала запустите сканирование.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Удалена пустая папка: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'Пустых папок в image/catalog/ не найдено.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Всего удалено пустых папок: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Кэш изображений успешно очищен.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Вотермарки работают в SMART-режиме: оригиналы не трогаются, логотип накладывается только при генерации кэша.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Сканирование image/catalog/ на дубликаты (по хешу MD5)... Это может занять время.';
|
||||||
|
$_['text_log_found_dupe'] = 'Найден дубликат: ';
|
||||||
|
$_['text_log_no_dupes'] = 'Дубликаты не найдены.';
|
||||||
|
$_['text_log_total_dupes'] = 'Найдено дубликатов: %s. Потенциальная экономия: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Нажмите "Исправить найденное" для перелинковки БД на оригинал и удаления дублей.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Исправлен и удален дубликат: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Успешно исправлено дубликатов: %s. Ссылки в БД обновлены.';
|
||||||
|
$_['text_log_scan_png'] = 'Поиск тяжелых PNG файлов без прозрачности...';
|
||||||
|
$_['text_log_no_png'] = 'Подходящих PNG файлов не найдено.';
|
||||||
|
$_['text_log_total_png'] = 'Найдено PNG без прозрачности: %s. Нажмите "Исправить" для конвертации в JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Сконвертировано в JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Успешно сконвертировано %s файлов. Освобождено: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Обновлено товаров заглушкой: ';
|
||||||
|
$_['text_log_ph_cat'] = 'Обновлено категорий заглушкой: ';
|
||||||
|
$_['text_log_ph_brand'] = 'Обновлено брендов заглушкой: ';
|
||||||
|
$_['text_log_ph_empty'] = 'Заглушки не выбраны в настройках.';
|
||||||
|
$_['text_log_scan_exif'] = 'Сканирование EXIF-данных (мета-теги камер)...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Очищен EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'Файлов с EXIF для очистки не найдено.';
|
||||||
|
$_['text_log_success_exif'] = 'Очищен EXIF у %s файлов. Освобождено: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Поиск фото размером менее %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Слишком маленькое (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'Мелких фото не найдено.';
|
||||||
|
$_['text_log_total_small'] = 'Найдено мелких фото: %s. Пожалуйста, замените их на качественные.';
|
||||||
|
$_['text_log_html_broken'] = 'Битый IMG в HTML таблицы %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'Битых картинок в HTML-описаниях не найдено.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Найдено битых ссылок в HTML: %s. Исправьте их вручную в редакторе.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Поиск потерянных файлов в image/cache/...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Удален потерянный кэш: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'Потерянного кэша не найдено.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Удалено файлов потерянного кэша: %s. Освобождено: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Поиск файлов с кириллицей или пробелами...';
|
||||||
|
$_['text_log_no_bad_names'] = 'Файлов с некорректными именами не найдено.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Найдено файлов с кириллицей/пробелами: %s. Нажмите "Исправить" для транслитерации и обновления БД.';
|
||||||
|
$_['text_log_renamed'] = 'Переименовано: ';
|
||||||
|
$_['text_log_success_translit'] = 'Успешно транслитерировано файлов: %s. БД обновлена.';
|
||||||
|
$_['text_log_scan_restore'] = 'Сканирование image/cache/ для восстановления утерянных оригиналов...';
|
||||||
|
$_['text_log_restored_cache'] = 'Восстановлено из кэша: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'Отсутствующих оригиналов в кэше не найдено.';
|
||||||
|
$_['text_log_success_restore'] = 'Успешно восстановлено оригиналов из кэша: %s.';
|
||||||
|
$_['error_invalid_folder'] = 'Ошибка: Недопустимое имя папки!';
|
||||||
|
$_['text_formats_title'] = 'Современные форматы (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Настройки интеграции современных форматов в файловую систему и шаблоны OpenCart.';
|
||||||
|
$_['entry_support_svg'] = 'Поддержка загрузки SVG';
|
||||||
|
$_['help_support_svg'] = 'Разрешает загрузку векторных SVG-изображений через стандартный Filemanager и их безопасный вывод на витрине без искажений размеров.';
|
||||||
|
$_['entry_support_webp'] = 'Поддержка загрузки WebP';
|
||||||
|
$_['help_support_webp'] = 'Позволяет вручную загружать, подвязывать и выводить на сайте готовые файлы .webp через Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP в TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Добавляет поддержку загрузки, предпросмотра и эскизов WebP в TrueFileManager от sitecreator (если он установлен).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Сверхбыстрая подмена: автоматически меняет расширения .jpg/.png на .webp прямо в HTML-выводе витрины и добавляет атрибут loading="lazy". Не трогает базу данных!';
|
||||||
|
$_['help_tool_broken_db'] = 'Ищет в базе данных картинки, физические файлы которых были удалены с сервера. Позволяет очистить битые ссылки.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Рекурсивно сканирует директорию image/catalog/ и безопасно удаляет все папки, в которых нет ни одного файла.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Строит дерево каталогов и показывает реальный вес каждой папки для поиска "пожирателей" места.';
|
||||||
|
$_['help_tool_html_broken'] = 'Сканирует HTML-описания товаров и категорий на наличие тегов <img> с несуществующими картинками.';
|
||||||
|
$_['help_tool_small_photos'] = 'Находит картинки с разрешением ниже заданного (например, меньше 300x300 px). Добавлены ссылки на поиск качественных аналогов в Google Images и Яндекс Картинках.';
|
||||||
|
$_['help_tool_duplicates'] = 'Ищет полные дубликаты файлов по MD5 хешу (разные имена, но одинаковая картинка). Позволяет склеить их в один файл и обновить БД.';
|
||||||
|
$_['help_tool_watermark'] = 'Наложение водяных знаков в SMART-режиме (только на кэш витрины, оригиналы не изменяются).';
|
||||||
|
$_['help_tool_placeholders'] = 'Массово устанавливает выбранную картинку-заглушку для товаров и категорий, у которых вообще нет фото.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Инструмент сканирует папку изображений и находит тяжелые PNG-файлы, которые не имеют прозрачного фона (альфа-канала). Поскольку прозрачность им не нужна, их можно безопасно конвертировать в формат JPG. Это значительно облегчает файлы (в среднем на 70-80%) и ускоряет загрузку сайта, а все ссылки в базе данных автоматически обновляются.';
|
||||||
|
$_['help_tool_exif'] = 'Сканирует JPEG-файлы и удаляет из них скрытые EXIF-данные (геотеги, данные о камере), уменьшая вес на 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Ищет в папке image/cache/ кэшированные файлы, оригиналы которых уже удалены из image/catalog/, и стирает этот мусор.';
|
||||||
|
$_['help_tool_translit'] = 'Находит файлы с кириллицей или пробелами в названиях, переименовывает их транслитом (tovar_1.jpg) и обновляет пути в БД.';
|
||||||
|
$_['help_tool_cache_restore'] = 'Если вы случайно удалили оригинал из catalog, инструмент попытается найти его копию в cache и восстановить обратно.';
|
||||||
|
$_['entry_quarantine'] = 'Переместить оригиналы в Карантин (вместо удаления)';
|
||||||
|
$_['entry_backup_db'] = 'Создать бэкап изменяемых таблиц в SQL перед запуском';
|
||||||
|
$_['button_fix_selected'] = 'ИСПРАВИТЬ ВЫБРАННЫЕ';
|
||||||
|
$_['button_export_log'] = 'Экспортировать в CSV';
|
||||||
|
$_['text_th_before'] = 'Состояние ДО';
|
||||||
|
$_['text_th_after'] = 'Ожидаемый результат ПОСЛЕ';
|
||||||
|
$_['text_th_select'] = 'Действие';
|
||||||
|
$_['text_th_preview'] = 'Предпросмотр';
|
||||||
|
$_['text_th_name'] = 'Объект / Файл';
|
||||||
|
$_['text_global_action'] = 'Групповое действие для выбранных:';
|
||||||
|
$_['text_action_clear'] = 'Очистить ссылку в БД';
|
||||||
|
$_['text_action_disable'] = 'Отключить (Статус = 0)';
|
||||||
|
$_['text_action_stock'] = 'Установить "Нет в наличии"';
|
||||||
|
$_['text_action_placeholder'] = 'Установить заглушку';
|
||||||
|
$_['text_action_ignore'] = 'Ничего не делать';
|
||||||
|
$_['text_action_merge'] = 'Склеить (удалить дубликат, оставить оригинал)';
|
||||||
|
$_['text_action_convert'] = 'Конвертировать в JPG';
|
||||||
|
$_['text_action_rename'] = 'Переименовать транслитом';
|
||||||
|
$_['text_action_delete'] = 'Удалить';
|
||||||
|
$_['text_action_restore'] = 'Восстановить';
|
||||||
|
$_['text_db_backup_created'] = 'Создан бэкап БД: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Перенесено в карантин: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Успешно выполнено';
|
||||||
|
$_['text_fix_status_error'] = 'Ошибка: %s';
|
||||||
|
$_['text_log_exported'] = 'Лог успешно экспортирован!';
|
||||||
|
$_['text_select_action'] = '-- Выберите действие --';
|
||||||
|
$_['error_empty_selection'] = 'Ошибка: Не выбрано ни одной строки для исправления!';
|
||||||
|
$_['text_link_admin'] = 'Админка';
|
||||||
|
$_['text_link_catalog'] = 'Витрина';
|
||||||
|
$_['entry_threads'] = 'Потоков обработки';
|
||||||
|
$_['text_threads_optimal'] = 'оптимально';
|
||||||
|
$_['button_download_zip'] = 'Скачать ZIP-архив и удалить папку';
|
||||||
|
$_['confirm_download_and_delete'] = 'Архив успешно скачался? Можно удалить временную папку копий на сервере?';
|
||||||
|
$_['button_clear_backups'] = 'Удалить бэкапы БД';
|
||||||
|
$_['button_clear_backups_short'] = 'Очистить бэкапы';
|
||||||
|
$_['confirm_clear_backups'] = 'Вы уверены, что хотите полностью удалить все SQL-бэкапы таблиц?';
|
||||||
|
$_['text_compare_title'] = 'Сравнение изображений ДО / ПОСЛЕ';
|
||||||
|
$_['text_compare_before'] = 'ДО';
|
||||||
|
$_['text_compare_after'] = 'ПОСЛЕ';
|
||||||
|
$_['text_backups_cleared'] = 'Все SQL-бэкапы таблиц успешно удалены!';
|
||||||
|
$_['error_no_backups'] = 'Бэкапы не найдены или папка пуста.';
|
||||||
|
$_['button_restore'] = 'Вернуть';
|
||||||
|
$_['button_empty'] = 'Очистить';
|
||||||
|
$_['entry_wm_targets'] = 'Применять водяной знак к:';
|
||||||
|
$_['text_wm_target_product'] = 'Фото товаров';
|
||||||
|
$_['text_wm_target_category'] = 'Фото категорий';
|
||||||
|
$_['text_wm_target_brand'] = 'Логотипы брендов';
|
||||||
|
$_['text_wm_target_banner'] = 'Баннеры и слайды';
|
||||||
|
$_['text_wm_target_blog'] = 'Статьи / Блог';
|
||||||
|
$_['error_no_scan_data'] = 'Ошибка: Данные сканирования не найдены. Сначала запустите сканирование!';
|
||||||
|
$_['error_row_not_found'] = 'Ошибка: Объект не найден в результатах сканирования!';
|
||||||
|
$_['error_file_not_found'] = 'Ошибка: Файл не найден на сервере!';
|
||||||
|
$_['text_folder_rules_title'] = 'Индивидуальные правила сжатия для папок';
|
||||||
|
$_['text_folder_rules_desc'] = 'Вы можете переопределить качество и максимальные размеры для конкретных папок. Если оставить поле пустым, будет использоваться глобальная настройка. Путь указывается относительно папки image (например, catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Путь к папке';
|
||||||
|
$_['entry_jpg_quality'] = 'Качество JPG';
|
||||||
|
$_['entry_webp_quality'] = 'Качество WebP';
|
||||||
|
$_['button_add_rule'] = 'Добавить правило';
|
||||||
|
$_['button_remove'] = 'Удалить';
|
||||||
|
$_['entry_wm_angle'] = 'Угол наклона водяного знака (градусы)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Скругление углов водяного знака (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Масштабирование водяного знака';
|
||||||
|
$_['entry_wm_size_percent'] = 'Размер водяного знака (% от ширины изображения)';
|
||||||
|
$_['entry_wm_text_color'] = 'Цвет текста водяного знака';
|
||||||
|
$_['entry_wm_text_font'] = 'Шрифт водяного знака (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Режим фильтрации Категорий/Брендов';
|
||||||
|
$_['text_wm_filter_include'] = 'Только для выбранных';
|
||||||
|
$_['text_wm_filter_exclude'] = 'Для всех, кроме выбранных';
|
||||||
|
$_['text_wm_size_original'] = 'Оригинальный размер';
|
||||||
|
$_['text_wm_size_percent'] = 'Пропорционально ширине изображения (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Предпросмотр водяного знака';
|
||||||
|
$_['text_wm_preview_title'] = 'Предпросмотр водяного знака';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Исключить папки из сканирования';
|
||||||
|
$_['text_cron_entities'] = 'Какие типы фото обрабатывать?';
|
||||||
|
$_['text_cron_quality_override'] = 'Переопределить параметры сжатия для Крона';
|
||||||
|
$_['text_cron_recommendations'] = 'Рекомендации по расписанию запуска Cron';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>Для небольших сайтов (до 5000 товаров)</b> рекомендуется запускать Крон каждый час (<code>0 * * * *</code>).</li><li><b>Для средних сайтов (5000-20000 товаров)</b> рекомендуется запускать раз в 3-6 часов (<code>0 */6 * * *</code>).</li><li><b>Для крупных сайтов (более 20000 товаров)</b> — один раз в сутки ночью (например, в 2:00: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Сканер битых/пустых файлов на сервере';
|
||||||
|
$_['help_tool_broken_files'] = 'Сканирует физические файлы в папке image/catalog/ на наличие поврежденных изображений или пустых файлов размером 0 байт.';
|
||||||
|
$_['text_grid_broken_file'] = 'Битый или пустой файл на сервере';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Удаление или замена заглушкой';
|
||||||
|
$_['text_description_broken_files'] = 'Результаты сканирования физических файлов в папке image/catalog/. Здесь отображаются битые (поврежденные) изображения или файлы размером 0 байт.';
|
||||||
|
$_['text_action_strip'] = 'Очистить EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Установить заглушку';
|
||||||
|
$_['text_th_entity'] = 'Объект / Связанные ссылки';
|
||||||
|
$_['text_cron_entity_product'] = 'Товары';
|
||||||
|
$_['text_cron_entity_category'] = 'Категории';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Производители / Бренды';
|
||||||
|
$_['text_cron_entity_banner'] = 'Баннеры';
|
||||||
|
$_['text_cron_entity_blog'] = 'Блог / Статьи';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'Качество JPG (Крон)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'Качество WebP (Крон)';
|
||||||
|
$_['entry_cron_max_width'] = 'Макс. ширина px (Крон)';
|
||||||
|
$_['entry_cron_max_height'] = 'Макс. высота px (Крон)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Папки для сканирования';
|
||||||
|
$_['text_no_fonts'] = 'TTF-шрифты не найдены в system/library/font/';
|
||||||
|
$_['button_apply'] = 'Применить';
|
||||||
|
$_['text_backup_will_be_created'] = 'Резервная копия базы данных будет автоматически создана перед выполнением этого действия.';
|
||||||
|
$_['text_bulk_action'] = 'Групповое действие для выбранных';
|
||||||
|
$_['text_grid_results'] = 'Результаты и детали сканирования';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Оригинальные файлы будут перемещены в карантин (image/catalog_trash/) перед изменением.';
|
||||||
|
$_['text_success_apply'] = 'Изменения успешно применены!';
|
||||||
|
$_['text_description_broken'] = 'Результаты сканирования базы данных на наличие битых ссылок (записи в таблицах с несуществующими файлами).';
|
||||||
|
$_['text_description_cache_restore'] = 'Результаты поиска файлов в кэше, оригиналы которых отсутствуют в каталоге.';
|
||||||
|
$_['text_description_duplicates'] = 'Результаты поиска полных дубликатов изображений по MD5-хешу.';
|
||||||
|
$_['text_description_empty_folders'] = 'Список пустых папок в директории image/catalog/ для удаления.';
|
||||||
|
$_['text_description_exif'] = 'Результаты поиска изображений с метаданными EXIF, которые можно очистить.';
|
||||||
|
$_['text_description_folder_tree'] = 'Статистика размера папок в каталоге image/catalog/.';
|
||||||
|
$_['text_description_html_broken'] = 'Результаты поиска несуществующих файлов картинок, на которые ссылаются описания в HTML.';
|
||||||
|
$_['text_description_placeholders'] = 'Результаты поиска сущностей без изображений для заполнения заглушками.';
|
||||||
|
$_['text_description_png_jpg'] = 'Результаты поиска тяжелых изображений PNG без прозрачности, которые можно пережать в JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'Список изображений, размеры которых меньше минимально заданных (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Результаты поиска файлов кэша, оригиналы которых уже были удалены.';
|
||||||
|
$_['text_description_translit'] = 'Результаты поиска файлов с кириллицей или пробелами в названиях.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Очистить ссылку или установить заглушку';
|
||||||
|
$_['text_grid_dupe_after'] = 'Оригинал (остаётся)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Дубликат (будет удалён)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Пустая папка';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Удалить пустую папку';
|
||||||
|
$_['text_grid_exif_after'] = 'Очистить метаданные';
|
||||||
|
$_['text_grid_exif_before'] = 'Содержит EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Исправить ссылку вручную';
|
||||||
|
$_['text_grid_missing_file'] = 'Файл отсутствует';
|
||||||
|
$_['text_grid_missing_html'] = 'Картинка не найдена в описании';
|
||||||
|
$_['text_grid_missing_orig'] = 'Оригинал отсутствует';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Восстановить оригинал из кэша';
|
||||||
|
$_['text_grid_no_image'] = 'Нет изображения (пусто)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Осиротевший кэш';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Удалить ненужный кэш';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Установить заглушку бренда';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Установить заглушку категории';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Установить заглушку товара';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Рекомендуется заменить на более крупное';
|
||||||
|
$_['text_grid_too_small'] = 'Слишком маленькое';
|
||||||
|
$_['text_no_relations'] = 'Нет связей в БД';
|
||||||
|
$_['text_action_archive'] = 'Архивировать (в ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Архивировать + Удалить';
|
||||||
|
$_['text_tool_sanitizer'] = 'Санитайзер имен файлов';
|
||||||
|
$_['help_tool_sanitizer'] = 'Массовая очистка имен файлов и папок от кириллицы, пробелов и спецсимволов, приведение расширений к единому виду.';
|
||||||
|
$_['text_description_sanitizer'] = 'Результаты анализа имен файлов для санитаризации по выбранным правилам.';
|
||||||
|
$_['entry_sz_translit'] = 'Транслитерация (кириллица в латиницу)';
|
||||||
|
$_['entry_sz_spaces'] = 'Заменять пробелы на символ "_"';
|
||||||
|
$_['entry_sz_special'] = 'Удалять специальные символы';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Имя файла в нижний регистр';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Расширение в нижний регистр';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Нормализовать расширения (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Режим фильтрации папок';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Сканировать все, кроме выбранных (Исключения)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Сканировать только выбранные папки';
|
||||||
|
$_['button_select_all'] = 'Выбрать все';
|
||||||
|
$_['button_deselect_all'] = 'Снять все';
|
||||||
|
$_['entry_wm_upload_font'] = 'Загрузить свой шрифт TTF';
|
||||||
|
$_['button_upload_font'] = 'Загрузить шрифт';
|
||||||
|
$_['text_success_font_upload'] = 'Шрифт успешно загружен!';
|
||||||
|
$_['error_font_upload'] = 'Ошибка загрузки шрифта! Разрешены только файлы .ttf весом до 5 МБ.';
|
||||||
|
$_['entry_lazy_load'] = 'Прогрессивная ленивая загрузка';
|
||||||
|
$_['help_lazy_load'] = 'Включает премиальную прогрессивную отложенную загрузку на витрине. Изображения заменяются микро-превью и плавно размываются (blur-up), подгружаясь по мере прокрутки.';
|
||||||
|
$_['confirm_replace_mode'] = 'ВНИМАНИЕ! Вы выбрали режим «Заменять оригиналы». Все изображения будут перезаписаны прямо на сервере. Настоятельно рекомендуем сделать резервную копию. Вы уверены, что хотите продолжить?';
|
||||||
|
$_['text_compare'] = 'Сравнить';
|
||||||
|
$_['text_home'] = 'Главная';
|
||||||
|
$_['text_tech_gd'] = 'Библиотека GD';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'Поддержка WebP';
|
||||||
|
$_['text_position_1'] = '1 - Слева вверху';
|
||||||
|
$_['text_position_2'] = '2 - По центру вверху';
|
||||||
|
$_['text_position_3'] = '3 - Справа вверху';
|
||||||
|
$_['text_position_4'] = '4 - Слева посередине';
|
||||||
|
$_['text_position_5'] = '5 - По центру';
|
||||||
|
$_['text_position_6'] = '6 - Справа посередине';
|
||||||
|
$_['text_position_7'] = '7 - Слева внизу';
|
||||||
|
$_['text_position_8'] = '8 - По центру внизу';
|
||||||
|
$_['text_position_9'] = '9 - Справа внизу';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Уровень лога';
|
||||||
|
$_['text_log_changed_only'] = 'Только изменённые';
|
||||||
|
$_['text_log_all'] = 'Все файлы (подробно)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Пропущен (нет улучшения)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Сбросить кэш оптимизации (полный пересканирование)';
|
||||||
|
$_['confirm_reset_cache'] = 'Сбросить кэш оптимизации? Следующее сканирование проверит ВСЕ файлы заново.';
|
||||||
|
$_['text_cache_cleared'] = 'Кэш оптимизации очищен. Следующее сканирование проверит все файлы.';
|
||||||
|
$_['error_wm_preview'] = 'Ошибка генерации превью водяного знака.';
|
||||||
|
$_['error_wm_too_large'] = 'Изображение водяного знака слишком большое (%dx%d px)! Максимально допустимый размер: %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'ВНИМАНИЕ: Некоторые файлы не удалось заменить (заблокированы или нет прав):';
|
||||||
|
$_['text_action_compress'] = 'Сжать изображения';
|
||||||
|
$_['text_estimated'] = 'Прогноз';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Мин. сжатие (байт)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Мин. сжатие (%)';
|
||||||
|
$_['text_optimized'] = 'Оптимизировано';
|
||||||
|
$_['entry_broken_fallback'] = 'Заглушка для битых фото';
|
||||||
|
$_['help_broken_fallback'] = 'Автоматически заменяет отсутствующие или поврежденные изображения каталога на витрине на изображение-заглушку.';
|
||||||
|
$_['entry_fallback_image'] = 'Изображение-заглушка';
|
||||||
|
$_['help_fallback_image'] = 'Выберите изображение, которое будет показано, если оригинальный файл картинки товара или категории отсутствует. По умолчанию используется no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'Конвертер CMYK в RGB';
|
||||||
|
$_['help_tool_cmyk'] = 'Конвертация JPEG изображений из цветового профиля CMYK в sRGB для корректного отображения в браузерах Safari и на устройствах iOS.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Поиск тяжелых файлов';
|
||||||
|
$_['help_tool_heavy_files'] = 'Поиск самых тяжелых оригинальных изображений в каталоге с возможностью их быстрого сжатия прямо здесь.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Макс. количество найденных файлов (всего)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Конвертировать в RGB';
|
||||||
|
$_['text_description_cmyk'] = 'Найдены файлы в цветовом пространстве CMYK. Конвертация в sRGB гарантирует корректное отображение на всех платформах, включая iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'Список из %d самых тяжелых файлов в каталоге изображений. Вы можете оптимизировать их прямо здесь.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Статус: Будут сканироваться все папки (исключения не выбраны).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Статус: Будут сканироваться все папки, КРОМЕ %d отмеченных.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Статус: Будут сканироваться только %d отмеченных папок.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'ВНИМАНИЕ: Ни одна папка не выбрана! Крон ничего не будет сканировать.';
|
||||||
|
$_['text_action_apply_changes'] = 'Применить указанные изменения';
|
||||||
|
$_['entry_wm_status'] = 'Статус водяного знака';
|
||||||
|
$_['text_disabled'] = 'Отключено';
|
||||||
|
$_['entry_rule_name'] = 'Название правила';
|
||||||
|
$_['entry_rule_status'] = 'Статус правила';
|
||||||
|
$_['entry_action'] = 'Действие';
|
||||||
|
$_['entry_ph_status'] = 'Статус заглушек';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Макс. файлов для поиска (лимит)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Минимальный размер';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Макс. файлов для поиска (лимит)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Минимальный размер';
|
||||||
|
$_['text_wm_rule_title'] = 'Правило водяного знака';
|
||||||
|
$_['button_close'] = 'Закрыть';
|
||||||
|
$_['text_ph_rule_title'] = 'Правило заглушки';
|
||||||
|
$_['entry_ph_image'] = 'Изображение заглушки';
|
||||||
|
$_['button_clear_cache_wm'] = 'Очистить кэш (применить водяной знак)';
|
||||||
|
$_['text_active'] = 'Активна';
|
||||||
|
$_['text_error'] = 'Не активна';
|
||||||
|
$_['text_th_dupe_delete'] = 'Файл для удаления (Дубликат)';
|
||||||
|
$_['text_th_dupe_keep'] = 'Файл для сохранения (Оригинал)';
|
||||||
|
$_['text_dupe_original_label'] = 'Оригинал';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Дубликат';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Использование диска';
|
||||||
|
$_['text_cumulative_stats'] = 'Статистика оптимизации';
|
||||||
|
$_['text_disk_catalog'] = 'Каталог изображений';
|
||||||
|
$_['text_disk_cache'] = 'Кэш OC';
|
||||||
|
$_['text_disk_trash'] = 'Корзина модуля';
|
||||||
|
$_['text_disk_other'] = 'Прочее';
|
||||||
|
$_['text_disk_free'] = 'Свободно';
|
||||||
|
$_['text_total_files_opt'] = 'Файлов обработано';
|
||||||
|
$_['text_total_saved'] = 'Всего сэкономлено';
|
||||||
|
$_['text_avg_saving_pct'] = 'Средняя экономия';
|
||||||
|
$_['text_stats_hint'] = 'Статистика накапливается по всем сессиям (хранится локально в браузере)';
|
||||||
|
$_['text_reset_stats'] = 'Сбросить статистику';
|
||||||
|
$_['text_reset_stats_confirm']= 'Сбросить накопленную статистику оптимизации?';
|
||||||
|
$_['text_export_import'] = 'Экспорт / Импорт настроек';
|
||||||
|
$_['button_export_settings'] = 'Экспорт настроек (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Импортировать настройки';
|
||||||
|
$_['help_export_settings'] = 'Скачать все настройки модуля в JSON-файл';
|
||||||
|
$_['help_import_settings'] = 'Загрузить ранее экспортированный JSON для восстановления настроек';
|
||||||
|
$_['text_import_success'] = 'Настройки успешно импортированы. Перезагрузите страницу для применения.';
|
||||||
|
$_['error_import_file'] = 'Файл не загружен или ошибка загрузки';
|
||||||
|
$_['error_import_invalid'] = 'Некорректный файл настроек (неверный модуль или формат)';
|
||||||
|
$_['text_click_to_load'] = 'Нажмите ↑ для загрузки данных диска';
|
||||||
|
$_['tab_formats'] = 'Форматы изображений';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Лимиты диска хостинга (вручную)';
|
||||||
|
$_['text_disk_quota_desc'] = 'Если график дискового пространства показывает неверные данные (физический диск сервера вместо лимита вашего хостинга), вы можете указать лимиты вашего тарифа вручную. Укажите 0, чтобы использовать автоопределение.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Выделено диска на хостинге (ГБ)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
|
||||||
|
$_['entry_auto_disk'] = 'Автообновление при входе';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Название задачи';
|
||||||
|
$_['entry_cron_task_summary'] = 'Параметры сжатия';
|
||||||
|
$_['button_add_cron_task'] = 'Добавить задачу';
|
||||||
|
$_['button_copy_cron_url'] = 'Копировать URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'Требуется лицензионный ключ';
|
||||||
|
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
|
||||||
|
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
|
||||||
|
$_['button_activate'] = 'Активировать модуль';
|
||||||
|
$_['text_support'] = 'Техническая поддержка';
|
||||||
|
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
|
||||||
|
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
|
||||||
|
$_['help_wm_max_height'] = 'Если исходное изображение водяного знака превышает эту высоту, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
|
||||||
|
$_['entry_disk_status'] = 'Включить сканирование диска и график';
|
||||||
|
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
|
||||||
|
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
|
||||||
|
|
@ -0,0 +1,567 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Стиснення оригіналів та видалення сміття';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Стиснення оригіналів та видалення сміття';
|
||||||
|
$_['text_extension'] = 'Розширення';
|
||||||
|
$_['text_edit'] = 'Налаштування модуля';
|
||||||
|
$_['text_success'] = 'Налаштування успішно збережено!';
|
||||||
|
$_['entry_warning'] = 'УВАГА: Обов`язково зробіть бекап файлів сайту і особливо папки /image/catalog перед початком роботи!';
|
||||||
|
$_['text_tech'] = 'Перевірка технологій сервера:';
|
||||||
|
$_['text_author'] = 'Технічна підтримка та побажання: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Оптимізація та Ресайз';
|
||||||
|
$_['tab_cleaner'] = 'Очищення невикористовуваних зображень';
|
||||||
|
$_['tab_info'] = 'Інфо / Екосистема [NAT]';
|
||||||
|
$_['tab_settings'] = 'Налаштування';
|
||||||
|
$_['tab_tools'] = 'Інструменти (pro)';
|
||||||
|
$_['tab_cron'] = 'Крон-завдання';
|
||||||
|
$_['entry_mode'] = 'Режим роботи';
|
||||||
|
$_['text_copy'] = 'Створити копію (безпечно)';
|
||||||
|
$_['text_replace'] = 'Замінювати оригінали (потребує бекап!)';
|
||||||
|
$_['entry_folder'] = 'Ім`я папки для копії';
|
||||||
|
$_['entry_batch'] = 'Файлів за прохід';
|
||||||
|
$_['entry_log_limit'] = 'Рядків логу на екрані';
|
||||||
|
$_['entry_max_width'] = 'Макс. ширина (px) [0 - без ліміту]';
|
||||||
|
$_['entry_max_height'] = 'Макс. висота (px) [0 - без ліміту]';
|
||||||
|
$_['entry_jpg'] = 'Якість JPG (0-100)';
|
||||||
|
$_['entry_png'] = 'Стиснення PNG (0-9)';
|
||||||
|
$_['entry_webp'] = 'Якість WebP (0-100)';
|
||||||
|
$_['entry_targets'] = 'Що оптимізувати?';
|
||||||
|
$_['text_all_catalog'] = '[ Весь каталог image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Файли тільки в корені image/catalog';
|
||||||
|
$_['entry_threshold'] = 'Поріг заміни (%)';
|
||||||
|
$_['help_threshold'] = '0: замінювати тільки якщо вага стала меншою. 10: дозволити збільшення ваги до 10%.';
|
||||||
|
$_['entry_token'] = 'Токен активації (на корінь домену)';
|
||||||
|
$_['entry_status'] = 'Статус модуля';
|
||||||
|
$_['entry_menu_position'] = 'Позиція меню [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Тільки в модулях';
|
||||||
|
$_['text_menu_header'] = 'У шапці (Header)';
|
||||||
|
$_['text_menu_sidebar'] = 'У бічному меню (Sidebar)';
|
||||||
|
$_['text_menu_both'] = 'Скрізь (Шапка + Сайдбар)';
|
||||||
|
$_['text_status_on'] = 'Увімкнено';
|
||||||
|
$_['text_status_off'] = 'Вимкнено';
|
||||||
|
$_['entry_engine'] = 'Рушій обробки зображень';
|
||||||
|
$_['text_engine_gd'] = 'GD (Стандарт)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (Висока якість, збереження профілів sRGB)';
|
||||||
|
$_['entry_license'] = 'Ліцензія';
|
||||||
|
$_['text_cleaner_info'] = 'Цей інструмент шукає файли, які фізично існують на диску, але ніде не використовуються в базі даних або файлах шаблону.';
|
||||||
|
$_['entry_clean_targets'] = 'В яких папках шукати сміття?';
|
||||||
|
$_['entry_extended_log'] = 'Розширений лог (показати використовувані photo і де вони знайдені)';
|
||||||
|
$_['entry_process_broken'] = 'Враховувати файли з битим кодуванням (імена з символом "?")';
|
||||||
|
$_['button_scan_trash'] = 'ЗНАЙТИ СМІТТЯ';
|
||||||
|
$_['button_quarantine'] = 'В Карантин (Безпечно)';
|
||||||
|
$_['button_delete_trash'] = 'Видалити назавжди';
|
||||||
|
$_['button_empty_quarantine'] = 'Очистити папку Карантину';
|
||||||
|
$_['button_restore_quarantine'] = 'Повернути з Карантину';
|
||||||
|
$_['text_scan_result'] = 'Результати сканування';
|
||||||
|
$_['text_trash_files'] = 'Сміттєвих файлів: ';
|
||||||
|
$_['text_trash_size'] = 'Потенційно звільниться: ';
|
||||||
|
$_['button_start'] = 'СТАРТ ОПТИМІЗАЦІЇ';
|
||||||
|
$_['button_pause'] = 'ПАУЗА';
|
||||||
|
$_['button_stop'] = 'ЗУПИНИТИ';
|
||||||
|
$_['button_continue'] = 'ПРОДОВЖИТИ';
|
||||||
|
$_['button_start_new'] = 'СТАРТ (Новий запуск)';
|
||||||
|
$_['button_download_log'] = 'Завантажити лог в TXT';
|
||||||
|
$_['button_download'] = 'Завантажити';
|
||||||
|
$_['button_save'] = 'Зберегти';
|
||||||
|
$_['button_cancel'] = 'Скасувати';
|
||||||
|
$_['text_progress'] = 'Прогрес:';
|
||||||
|
$_['text_old_size'] = 'Було:';
|
||||||
|
$_['text_new_size'] = 'Стало:';
|
||||||
|
$_['text_savings'] = 'Економія:';
|
||||||
|
$_['text_detailed_log'] = 'Детальний лог';
|
||||||
|
$_['text_page'] = 'Стор.';
|
||||||
|
$_['text_waiting'] = 'Очікування запуску...';
|
||||||
|
$_['text_enabled'] = 'Увімкнено';
|
||||||
|
$_['text_disabled_error'] = 'Вимкнено (ПОМИЛКА)';
|
||||||
|
$_['text_supported'] = 'Підтримується';
|
||||||
|
$_['text_no'] = 'Ні';
|
||||||
|
$_['error_permission'] = 'У вас немає прав для керування модулем!';
|
||||||
|
$_['error_license'] = 'Помилка доступу: Невірний токен активації для цього домену!';
|
||||||
|
$_['error_domain'] = ' (Домен: ';
|
||||||
|
$_['text_found'] = 'Знайдено файлів: ';
|
||||||
|
$_['text_compressed'] = 'Стиснуто';
|
||||||
|
$_['text_original'] = 'Оригінал';
|
||||||
|
$_['text_error_process'] = 'Помилка обробки: ';
|
||||||
|
$_['error_log_empty'] = 'Лог порожній! Спочатку запустіть процес.';
|
||||||
|
$_['text_report_header'] = '=== ЗВІТ ===';
|
||||||
|
$_['text_total_files'] = 'Всього файлів: ';
|
||||||
|
$_['text_searching'] = 'Пошук файлів...';
|
||||||
|
$_['error_ajax'] = 'Помилка: ';
|
||||||
|
$_['text_resumed'] = '▶ ПРОЦЕС ВІДНОВЛЕНО...';
|
||||||
|
$_['text_paused'] = '⏸ ПАУЗА. Процес призупинено.';
|
||||||
|
$_['confirm_stop'] = 'Ви впевнені, що хочете повністю перервати процес?';
|
||||||
|
$_['confirm_delete'] = 'УВАГА! Файли будуть видалені з сервера назавжди без можливості відновлення! Продовжити?';
|
||||||
|
$_['confirm_empty_q'] = 'Ви впевнені, що хочете повністю очистити папку карантину?';
|
||||||
|
$_['confirm_restore'] = 'Всі файли з папки карантину будуть повернуті на свої місця. Продовжити?';
|
||||||
|
$_['button_stopping'] = 'Зупинка...';
|
||||||
|
$_['text_stopped_user'] = '🛑 ПРОЦЕС ПЕРЕРВАНО КОРИСТУВАЧЕМ!';
|
||||||
|
$_['text_increase'] = 'Збільшення';
|
||||||
|
$_['text_done'] = '✅ ГОТОВО! Процес завершено.';
|
||||||
|
$_['error_timeout'] = 'Таймаут сервера. Автоповтор через 3 секунди...';
|
||||||
|
$_['text_building_index'] = 'Крок 1/2: Індексація БД та шаблонів (пошук фото, що використовуються)...';
|
||||||
|
$_['text_comparing'] = 'Крок 2/2: Порівняння фізичних файлів з індексом...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Процес перенесення в image/catalog_trash/ завершено!';
|
||||||
|
$_['text_deleted_done'] = '✅ Процес видалення сміття завершено!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Папку карантину успішно видалено з сервера!';
|
||||||
|
$_['text_restore_done'] = '✅ Файли успішно повернуто з карантину в робочу папку!';
|
||||||
|
$_['text_quarantine_move'] = 'В карантині: ';
|
||||||
|
$_['text_deleted_file'] = 'Видалено: ';
|
||||||
|
$_['text_no_trash'] = 'У вибраних папках сміття не знайдено! Всі файли використовуються.';
|
||||||
|
$_['text_scanning_dirs'] = 'Скануємо директорії: ';
|
||||||
|
$_['text_log_used'] = '[ВИКОРИСТОВУЄТЬСЯ]';
|
||||||
|
$_['text_log_trash'] = '[СМІТТЯ]';
|
||||||
|
$_['text_log_deleted'] = '[ВИДАЛЕНО]';
|
||||||
|
$_['text_log_del_err'] = '[ПОМИЛКА ВИДАЛЕННЯ]';
|
||||||
|
$_['text_log_quarantine'] = '[В КАРАНТИН]';
|
||||||
|
$_['text_log_q_err'] = '[ПОМИЛКА ПЕРЕНЕСЕННЯ]';
|
||||||
|
$_['text_log_restored'] = '[ВІДНОВЛЕНО]';
|
||||||
|
$_['text_log_rest_err'] = '[ПОМИЛКА ВІДНОВЛЕННЯ]';
|
||||||
|
$_['text_used_in'] = 'у: ';
|
||||||
|
$_['text_analyzing'] = 'Аналіз бази даних...';
|
||||||
|
$_['text_clean_empty'] = 'Чисто! Сміття немає.';
|
||||||
|
$_['text_processing'] = 'Обробка...';
|
||||||
|
$_['text_restoring'] = 'Відновлення файлів...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (Та всі вкладені)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Тільки корінь)';
|
||||||
|
$_['text_q_empty_err'] = 'Папка карантину порожня або не існує.';
|
||||||
|
$_['error_token'] = 'Невірний токен. Перезавантажте сторінку.';
|
||||||
|
$_['text_apply_title'] = 'Оптимізацію завершено в папку: ';
|
||||||
|
$_['text_apply_info'] = 'Перевірте результат. Якщо все влаштовує — застосуйте зміни до основного каталогу.';
|
||||||
|
$_['entry_apply_backup'] = 'Перенести зі створенням бекапу (в Карантин)';
|
||||||
|
$_['entry_apply_replace'] = 'Замінити оригінали безповоротно';
|
||||||
|
$_['button_apply_main'] = 'ЗАСТОСУВАТИ ДО ОСНОВНОГО КАТАЛОГУ';
|
||||||
|
$_['button_delete_copies'] = 'Видалити тимчасові копії (скасування)';
|
||||||
|
$_['button_full_cleanup'] = 'Повне очищення всіх тимчасових папок та бекапів';
|
||||||
|
$_['text_apply_success'] = '✅ Файли успішно перенесено. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Оригінали збережено в папку: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Оригінали видалено.';
|
||||||
|
$_['text_error_source'] = 'Помилка: Папку з оптимізованими файлами не знайдено.';
|
||||||
|
$_['confirm_full_cleanup'] = 'УВАГА! Ця дія безповоротно видалить УСІ папки бекапів (backup_*) та тимчасові папки з копіями. Продовжити?';
|
||||||
|
$_['text_tools_desc'] = 'Глобальні сканери та інструменти для роботи з файловою системою і БД зображень.';
|
||||||
|
$_['text_tool_select'] = 'Виберіть інструмент:';
|
||||||
|
$_['text_group_scanners'] = 'Сканери та Аналіз';
|
||||||
|
$_['text_group_generators'] = 'Генератори та Обробка';
|
||||||
|
$_['text_group_experimental'] = 'Експериментальні функції (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Увага: Експериментальні функції зачіпають системні файли або базу даних. Настійно рекомендується зробити бекап!';
|
||||||
|
$_['text_tool_broken_db'] = 'Сканер битих/порожніх фото БД';
|
||||||
|
$_['text_tool_watermark'] = 'Динамічний Вотермарк (Тільки на кеш)';
|
||||||
|
$_['text_tool_duplicates'] = 'Поиск дубликатов файлов (за MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Конвертер важких PNG у JPG (сканер файлів без прозорості)';
|
||||||
|
$_['text_tool_placeholders'] = 'Генератор заглушок (Замість No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Очищення EXIF-даних (Геотеги/Мета)';
|
||||||
|
$_['text_tool_small_photos'] = 'Сканер занадто дрібних фото';
|
||||||
|
$_['text_tool_html_broken'] = 'Пошук битих картинок в HTML (Описах)';
|
||||||
|
$_['text_tool_empty_folders'] = 'Очищення порожніх папок (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Розумне очищення кешу зображень';
|
||||||
|
$_['text_tool_folder_tree'] = 'Дерево папок (Статистика ваги)';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (Без БД)';
|
||||||
|
$_['text_tool_translit'] = 'Транслітерація назв файлів';
|
||||||
|
$_['text_tool_cache_restore'] = 'Відновлення оригіналів із Кешу';
|
||||||
|
$_['entry_wm_type'] = 'Тип водяного знака';
|
||||||
|
$_['text_wm_image'] = 'Зображення (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Текст';
|
||||||
|
$_['entry_wm_image_path'] = 'Шлях до зображення';
|
||||||
|
$_['entry_wm_text_val'] = 'Текст водяного знака';
|
||||||
|
$_['entry_wm_position'] = 'Позиція (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Прозорість (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Тільки для Категорій';
|
||||||
|
$_['entry_wm_brand'] = 'Тільки для Брендів';
|
||||||
|
$_['entry_min_width'] = 'Мінімальна ширина (px)';
|
||||||
|
$_['entry_min_height'] = 'Мінімальна висота (px)';
|
||||||
|
$_['entry_ph_product'] = 'Заглушка для Товарів';
|
||||||
|
$_['entry_ph_category'] = 'Заглушка для Категорій';
|
||||||
|
$_['entry_ph_brand'] = 'Заглушка для Брендів';
|
||||||
|
$_['button_run_tool'] = 'Запустити інструмент';
|
||||||
|
$_['button_scan_tool'] = 'Сканувати';
|
||||||
|
$_['button_fix_tool'] = 'Виправити знайдене';
|
||||||
|
$_['text_th_path'] = 'Файл / Шлях';
|
||||||
|
$_['text_th_size'] = 'Розмір';
|
||||||
|
$_['text_th_action'] = 'Дія';
|
||||||
|
$_['text_th_status'] = 'Статус';
|
||||||
|
$_['text_th_problem'] = 'Проблема / Значення';
|
||||||
|
$_['text_cron_title'] = 'Налаштування Cron';
|
||||||
|
$_['text_cron_desc'] = 'Додайте команду в Cron хостингу для автоматичного фонового стиснення.';
|
||||||
|
$_['entry_cron_new_only'] = 'Обробляти тільки нові (необроблені) файли';
|
||||||
|
$_['entry_cron_folders'] = 'Папки для сканування (залиште порожнім для всього каталогу)';
|
||||||
|
$_['text_promo_title'] = 'Інші модулі серії [NAT]';
|
||||||
|
$_['text_promo_desc'] = 'Модулі серії [NAT] – це інструменти для глибокої оптимізації OpenCart. Ми фокусуємося на автоматизації рутини, прискоренні бази даних та очищенні серверів від сміття. Усі модулі мають відкритий вихідний код та єдину логіку управління.';
|
||||||
|
$_['text_support'] = 'Технічна підтримка та побажання:';
|
||||||
|
$_['button_more'] = 'Дивитися всі модулі на OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Приховування порожніх категорій і брендів + SEO сортування товарів (у наявності зверху) + Очищення сміття БД.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Новинки за датами';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Автоматична SEO-сторінка новинок. Відображає новинки суворо за реальною датою додавання. Слайдери, сітка та розумний сайдбар архіву дат.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Поточний модуль.</strong> Стиснення та ресайз ОРИГІНАЛІВ фото (image/catalog) у WebP + Очищення хостингу від невикористаних зображень.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Пошук і чищення зовнішніх посилань і HTTP-картинок у БД та файлах теми. Ідеально для HTTPS або очищення після парсингу.';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Створення опитувань та голосувань із візуальним конструктором дизайну, глибокою статистикою та захистом від накруток.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Інтелектуальне очищення сміття в базі даних, управління системними логами та додавання критично важливих індексів для прискорення.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Швидкий імпорт/експорт цін і залишків з файлів xls/xlsx/csv/xml/json або за посиланням. Маппінг полів, робота по Cron, текстові функції.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Зручні інструменти адміністратора: вхід під клієнтом, кастомізація адмінки, управління правами користувачів та приховування непотрібних пунктів меню.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Автоматичне відстеження UTM-міток при оформленні замовлення. Зберігає джерело переходу, кампанію, ключові слова та виводить ці дані в замовленні для аналітики реклами.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Верхній рекламний інфо-баннер, інформаційна смуга над шапкою, рухомий рядок та топ-баннер';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Створення інтерактивних промо-панелей та слайд-шоу в шапці сайту з конструктором блоків, таймерами, формою підписки та гнучким націлюванням.';
|
||||||
|
$_['text_tool_no_image'] = 'Товари без головного фото (порожньо в БД)';
|
||||||
|
$_['text_problem_main_broken'] = 'Бите головне фото (файл видалено)';
|
||||||
|
$_['text_problem_add_broken'] = 'Бите дод. фото (файл видалено)';
|
||||||
|
$_['text_problem_no_image'] = 'Відсутнє головне фото';
|
||||||
|
$_['text_err_dir'] = 'Помилка: Директорію не знайдено або невірний шлях.';
|
||||||
|
$_['text_err_no_queue'] = 'Помилка: Чергу не знайдено. Спочатку запустіть сканування.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Видалено порожню папку: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'Порожніх папок в image/catalog/ не знайдено.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Всього видалено порожніх папок: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Кеш зображень успішно очищено.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Вотермарки працюють в SMART-режимі: оригінали не зачіпаються, логотип накладається лише при генерації кешу.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Сканування image/catalog/ на дублікати (за хешем MD5)... Це може зайняти час.';
|
||||||
|
$_['text_log_found_dupe'] = 'Знайдено дублікат: ';
|
||||||
|
$_['text_log_no_dupes'] = 'Дублікатів не знайдено.';
|
||||||
|
$_['text_log_total_dupes'] = 'Знайдено дублікатів: %s. Потенційна економія: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Натисніть "Виправити знайдене" для перелінкування БД на оригинал та видалення дублів.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Виправлено та видалено дублікат: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Успішно виправлено дублікатів: %s. Посилання в БД оновлено.';
|
||||||
|
$_['text_log_scan_png'] = 'Пошук важких PNG файлів без прозорості...';
|
||||||
|
$_['text_log_no_png'] = 'Відповідних PNG файлів не знайдено.';
|
||||||
|
$_['text_log_total_png'] = 'Знайдено PNG без прозорості: %s. Натисніть "Виправити" для конвертації в JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Сконвертовано в JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Успішно сконвертовано %s файлів. Звільнено: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Оновлено товарів заглушкою: ';
|
||||||
|
$_['text_log_ph_cat'] = 'Оновлено категорій заглушкою: ';
|
||||||
|
$_['text_log_ph_brand'] = 'Оновлено брендів заглушкою: ';
|
||||||
|
$_['text_log_ph_empty'] = 'Заглушки не вибрані в налаштуваннях.';
|
||||||
|
$_['text_log_scan_exif'] = 'Сканування EXIF-даних (мета-теги камер)...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Очищено EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'Файлів з EXIF для очищення не знайдено.';
|
||||||
|
$_['text_log_success_exif'] = 'Очищено EXIF у %s файлів. Звільнено: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Пошук фото розміром менше %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Занадто мале (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'Дрібних фото не знайдено.';
|
||||||
|
$_['text_log_total_small'] = 'Знайдено дрібних фото: %s. Будь ласка, замініть їх на якісні.';
|
||||||
|
$_['text_log_html_broken'] = 'Битией IMG в HTML таблиці %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'Битих картинок в HTML-описах не знайдено.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Знайдено битих посилань в HTML: %s. Виправте їх вручну.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Пошук втрачених файлів у image/cache/...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Видалено втрачений кеш: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'Втраченого кешу не знайдено.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Видалено файлів втраченого кешу: %s. Звільнено: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Пошук файлів з кирилицею або пробілами...';
|
||||||
|
$_['text_log_no_bad_names'] = 'Файлів з некоректними іменами не знайдено.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Знайдено файлів з кирилицею/пробілами: %s. Натисніть "Виправити" для транслітерації та оновлення БД.';
|
||||||
|
$_['text_log_renamed'] = 'Перейменовано: ';
|
||||||
|
$_['text_log_success_translit'] = 'Успішно транслітеровано файлів: %s. БД оновлено.';
|
||||||
|
$_['text_log_scan_restore'] = 'Сканирование image/cache/ для відновлення втрачених оригіналів...';
|
||||||
|
$_['text_log_restored_cache'] = 'Відновлено з кешу: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'Відсутніх оригіналів у кеші не знайдено.';
|
||||||
|
$_['text_log_success_restore'] = 'Успішно відновлено оригіналів з кешу: %s.';
|
||||||
|
$_['error_invalid_folder'] = 'Помилка: Неприпустиме ім\'я папки!';
|
||||||
|
$_['text_formats_title'] = 'Сучасні формати (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Налаштування інтеграції сучасних форматів у файлову систему та шаблони OpenCart.';
|
||||||
|
$_['entry_support_svg'] = 'Підтримка завантаження SVG';
|
||||||
|
$_['help_support_svg'] = 'Дозволяє завантаження векторних SVG-зображень через стандартний Filemanager та їх безпечне виведення на вітрині без спотворення розмірів.';
|
||||||
|
$_['entry_support_webp'] = 'Підтримка завантаження WebP';
|
||||||
|
$_['help_support_webp'] = 'Дозволяє вручну завантажувати, підв\'язувати та виводити на сайті готові файли .webp через Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP у TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Додає підтримку завантаження, попереднього перегляду та ескізів WebP у TrueFileManager від sitecreator (якщо його встановлено).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Надшвидка підміна: автоматично змінює розширення .jpg/.png на .webp прямо в HTML-виводі вітрини та додає атрибут loading="lazy". Не чіпає базу даних!';
|
||||||
|
$_['help_tool_broken_db'] = 'Шукає в базі даних зображення, фізичні файли яких були видалені з сервера. Дозволяє очистити биті посилання.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Рекурсивно сканує директорію image/catalog/ та безпечно видаляє всі папки, в яких немає жодного файлу.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Будує дерево каталогів та показує реальну вагу кожної папки для пошуку "пожирачів" місця.';
|
||||||
|
$_['help_tool_html_broken'] = 'Сканує HTML-описи товарів та категорій на наявність тегів <img> з неіснуючими зображеннями.';
|
||||||
|
$_['help_tool_small_photos'] = 'Знаходить зображення з роздільною здатністю нижче заданої. Додано посилання на пошук якісних аналогів у Google Images та Яндекс Зображеннях.';
|
||||||
|
$_['help_tool_duplicates'] = 'Шукає повні дублікати файлів за MD5-хешем (різні назви, але однакова картинка). Дозволяє склеїти їх в один файл та оновити БД.';
|
||||||
|
$_['help_tool_watermark'] = 'Накладення водяних знаків у SMART-режимі (тільки на кеш вітрини, оригінали не змінюються).';
|
||||||
|
$_['help_tool_placeholders'] = 'Масово встановлює вибране зображення-заглушку для товарів та категорій, у яких взагалі немає фото.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Інструмент сканує папку зображень і знаходить важкі PNG-файли, які не мають прозорого фону (альфа-каналу). Оскільки прозорість їм не потрібна, їх можна безпечно конвертувати у формат JPG. Це значно полегшує файли (в середньому на 70-80%) та прискорює завантаження сайту, а всі посилання в базі даних автоматично оновлюються.';
|
||||||
|
$_['help_tool_exif'] = 'Сканує EXIF-дані та видаляє з них приховані теги, зменшуючи вагу на 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Шукає в папці image/cache/ кешовані файли, оригінали яких уже видалені, та очищає неактуальний кеш.';
|
||||||
|
$_['help_tool_translit'] = 'Знаходить файли з кирилицею або пробілами, перейменовує їх транслітом та оновлює шляхи в БД.';
|
||||||
|
$_['help_tool_cache_restore'] = 'Якщо ви випадково видалили оригінал з catalog, інструмент спробує знайти його копію в cache та відновити назад.';
|
||||||
|
$_['entry_quarantine'] = 'Перемістити оригінали в Карантин (замість видалення)';
|
||||||
|
$_['entry_backup_db'] = 'Створити бекап змінюваних таблиць у SQL перед запуском';
|
||||||
|
$_['button_fix_selected'] = 'ВИПРАВИТИ ВИБРАНІ';
|
||||||
|
$_['button_export_log'] = 'Експортувати в CSV';
|
||||||
|
$_['text_th_before'] = 'Стан ДО';
|
||||||
|
$_['text_th_after'] = 'Очікуваний результат ПІСЛЯ';
|
||||||
|
$_['text_th_select'] = 'Дія';
|
||||||
|
$_['text_th_preview'] = 'Передперегляд';
|
||||||
|
$_['text_th_name'] = 'Об`єкт / Файл';
|
||||||
|
$_['text_global_action'] = 'Групова дія для вибраних:';
|
||||||
|
$_['text_action_clear'] = 'Очистити посилання в БД';
|
||||||
|
$_['text_action_disable'] = 'Вимкнути (Статус = 0)';
|
||||||
|
$_['text_action_stock'] = 'Установити "Немає в наявності"';
|
||||||
|
$_['text_action_placeholder'] = 'Встановити заглушку';
|
||||||
|
$_['text_action_ignore'] = 'Нічого не робити';
|
||||||
|
$_['text_action_merge'] = 'Склеїти (видалити дублікат, залишити оригінал)';
|
||||||
|
$_['text_action_convert'] = 'Конвертувати в JPG';
|
||||||
|
$_['text_action_rename'] = 'Перейменувати транслітом';
|
||||||
|
$_['text_action_delete'] = 'Видалити';
|
||||||
|
$_['text_action_restore'] = 'Відновити';
|
||||||
|
$_['text_db_backup_created'] = 'Створено бекап БД: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Перенесено в карантин: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Успішно виконано';
|
||||||
|
$_['text_fix_status_error'] = 'Помилка: %s';
|
||||||
|
$_['text_log_exported'] = 'Лог успішно експортовано!';
|
||||||
|
$_['text_select_action'] = '-- Виберіть дію --';
|
||||||
|
$_['error_empty_selection'] = 'Помилка: Не вибрано жодного рядка для виправлення!';
|
||||||
|
$_['text_link_admin'] = 'Адмінка';
|
||||||
|
$_['text_link_catalog'] = 'Вітрина';
|
||||||
|
$_['entry_threads'] = 'Потоків обробки';
|
||||||
|
$_['text_threads_optimal'] = 'оптимально';
|
||||||
|
$_['button_download_zip'] = 'Завантажити ZIP-архів та видалити папку';
|
||||||
|
$_['confirm_download_and_delete'] = 'Архів успішно завантажився? Можна видалити тимчасову папку копій на сервері?';
|
||||||
|
$_['button_clear_backups'] = 'Видалити бекапи БД';
|
||||||
|
$_['button_clear_backups_short'] = 'Очистити бекапи';
|
||||||
|
$_['confirm_clear_backups'] = 'Ви впевнені, що хочете повністю видалити всі SQL-бекапи таблиць?';
|
||||||
|
$_['text_compare_title'] = 'Порівняння зображень ДО / ПІСЛЯ';
|
||||||
|
$_['text_compare_before'] = 'ДО';
|
||||||
|
$_['text_compare_after'] = 'ПІСЛЯ';
|
||||||
|
$_['text_backups_cleared'] = 'Усі SQL-бекапи таблиць успішно видалені!';
|
||||||
|
$_['error_no_backups'] = 'Бекапи не знайдені або папка порожня.';
|
||||||
|
$_['button_restore'] = 'Повернути';
|
||||||
|
$_['button_empty'] = 'Очистити';
|
||||||
|
$_['entry_wm_targets'] = 'Застосовувати водяний знак до:';
|
||||||
|
$_['text_wm_target_product'] = 'Фото товарів';
|
||||||
|
$_['text_wm_target_category'] = 'Фото категорій';
|
||||||
|
$_['text_wm_target_brand'] = 'Логотипи брендів';
|
||||||
|
$_['text_wm_target_banner'] = 'Банери та слайди';
|
||||||
|
$_['text_wm_target_blog'] = 'Статті / Блог';
|
||||||
|
$_['error_no_scan_data'] = 'Помилка: Дані сканування не знайдені. Спочатку запустіть сканування!';
|
||||||
|
$_['error_row_not_found'] = 'Помилка: Об`єкт не знайдено в результатах сканування!';
|
||||||
|
$_['error_file_not_found'] = 'Помилка: Файл не знайдено на сервері!';
|
||||||
|
$_['text_folder_rules_title'] = 'Індивідуальні правила стиснення для папок';
|
||||||
|
$_['text_folder_rules_desc'] = 'Ви можете перевизначити якість та максимальні розміри для конкретних папок. Якщо залишити поле порожнім, використовуватиметься глобальне налаштування. Шлях вказується відносно папки image (наприклад, catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Шлях до папки';
|
||||||
|
$_['entry_jpg_quality'] = 'Якість JPG';
|
||||||
|
$_['entry_webp_quality'] = 'Якість WebP';
|
||||||
|
$_['button_add_rule'] = 'Додати правило';
|
||||||
|
$_['button_remove'] = 'Вилучити';
|
||||||
|
$_['entry_wm_angle'] = 'Кут нахилу водяного знака (градуси)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Закруглення кутів водяного знака (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Масштабування водяного знака';
|
||||||
|
$_['entry_wm_size_percent'] = 'Розмір водяного знака (% від ширини зображення)';
|
||||||
|
$_['entry_wm_text_color'] = 'Колір тексту водяного знака';
|
||||||
|
$_['entry_wm_text_font'] = 'Шрифт водяного знака (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Режим фільтрації Категорій/Брендів';
|
||||||
|
$_['text_wm_filter_include'] = 'Тільки для вибраних';
|
||||||
|
$_['text_wm_filter_exclude'] = 'Для всіх, крім вибраних';
|
||||||
|
$_['text_wm_size_original'] = 'Оригінальний розмір';
|
||||||
|
$_['text_wm_size_percent'] = 'Пропорційно ширині зображення (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Попередній перегляд';
|
||||||
|
$_['text_wm_preview_title'] = 'Попередній перегляд водяного знака';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Виключити папки зі сканування';
|
||||||
|
$_['text_cron_entities'] = 'Які типи фото обробляти?';
|
||||||
|
$_['text_cron_quality_override'] = 'Перевизначити параметри стиснення для Крона';
|
||||||
|
$_['text_cron_recommendations'] = 'Рекомендації щодо розкладу Cron';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>Для невеликих сайтів (до 5000 товарів)</b> рекомендується запускати Крон кожну годину (<code>0 * * * *</code>).</li><li><b>Для середніх сайтів (5000-20000 товарів)</b> рекомендується запускати раз на 3-6 годин (<code>0 */6 * * *</code>).</li><li><b>Для великих сайтів (понад 20000 товарів)</b> — один раз на добу вночі (наприклад, о 2:00: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Сканер битих/порожніх файлів на сервері';
|
||||||
|
$_['help_tool_broken_files'] = 'Сканує фізичні файли в папці image/catalog/ на наявність пошкоджених зображень або порожніх файлів розміром 0 байт.';
|
||||||
|
$_['text_grid_broken_file'] = 'Битий або порожній файл на сервері';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Вилучення або заміна заглушкою';
|
||||||
|
$_['text_description_broken_files'] = 'Результати сканування фізичних файлів у папці image/catalog/. Тут відображаються биті (пошкоджені) зображення або файли розміром 0 байт.';
|
||||||
|
$_['text_action_strip'] = 'Очистити EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Встановити заглушку';
|
||||||
|
$_['text_th_entity'] = 'Об`єкт / Пов`язані посилання';
|
||||||
|
$_['text_cron_entity_product'] = 'Товари';
|
||||||
|
$_['text_cron_entity_category'] = 'Категорії';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Виробники / Бренди';
|
||||||
|
$_['text_cron_entity_banner'] = 'Банери';
|
||||||
|
$_['text_cron_entity_blog'] = 'Блог / Статті';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'Якість JPG (Крон)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'Якість WebP (Крон)';
|
||||||
|
$_['entry_cron_max_width'] = 'Макс. ширина px (Крон)';
|
||||||
|
$_['entry_cron_max_height'] = 'Макс. висота px (Крон)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Папки для сканування';
|
||||||
|
$_['text_no_fonts'] = 'TTF-шрифти не знайдено в system/library/font/';
|
||||||
|
$_['button_apply'] = 'Застосувати';
|
||||||
|
$_['text_backup_will_be_created'] = 'Резервна копія бази даних буде автоматично створена перед виконанням цієї дії.';
|
||||||
|
$_['text_bulk_action'] = 'Групова дія для вибраних';
|
||||||
|
$_['text_grid_results'] = 'Результати та деталі сканування';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Оригінальні файли будуть переміщені в карантин (image/catalog_trash/) перед зміною.';
|
||||||
|
$_['text_success_apply'] = 'Зміни успішно застосовані!';
|
||||||
|
$_['text_description_broken'] = 'Результати сканування бази даних на наявність битих посилань (записи в таблицях з неіснуючими файлами).';
|
||||||
|
$_['text_description_cache_restore'] = 'Результати пошуку файлів у кеші, оригінали яких відсутні в каталозі.';
|
||||||
|
$_['text_description_duplicates'] = 'Результати пошуку повних дублікатів зображень за MD5-хешем.';
|
||||||
|
$_['text_description_empty_folders'] = 'Список порожніх папок в директорії image/catalog/ для видалення.';
|
||||||
|
$_['text_description_exif'] = 'Результати пошуку зображень з метаданими EXIF, які можна очистити.';
|
||||||
|
$_['text_description_folder_tree'] = 'Статистика розміру папок у каталозі image/catalog/.';
|
||||||
|
$_['text_description_html_broken'] = 'Результати пошуку неіснуючих файлів картинок, на які посилаються описи в HTML.';
|
||||||
|
$_['text_description_placeholders'] = 'Результати пошуку сутностей без зображень для заповнення заглушками.';
|
||||||
|
$_['text_description_png_jpg'] = 'Результати пошуку важких зображень PNG без прозорості, які можна перетиснути в JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'Список зображень, розміри яких менше мінімально заданих (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Результати пошуку файлів кешу, оригінали яких уже було видалено.';
|
||||||
|
$_['text_description_translit'] = 'Результати пошуку файлів з кирилицею або пробілами в назвах.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Очистити посилання або встановити заглушку';
|
||||||
|
$_['text_grid_dupe_after'] = 'Оригінал (залишається)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Дублікат (буде видалено)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Порожня папка';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Вилучити порожню папку';
|
||||||
|
$_['text_grid_exif_after'] = 'Очистити метадані';
|
||||||
|
$_['text_grid_exif_before'] = 'Містить EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Виправити посилання вручну';
|
||||||
|
$_['text_grid_missing_file'] = 'Файл відсутній';
|
||||||
|
$_['text_grid_missing_html'] = 'Картинка не знайдена в описі';
|
||||||
|
$_['text_grid_missing_orig'] = 'Оригінал відсутній';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Відновити оригінал з кешу';
|
||||||
|
$_['text_grid_no_image'] = 'Немає зображення (порожньо)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Осиротілий кеш';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Видалити непотрібний кеш';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Встановити заглушку бренду';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Встановити заглушку категорії';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Встановити заглушку товару';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Рекомендується замінити на більше';
|
||||||
|
$_['text_grid_too_small'] = 'Занадто мале';
|
||||||
|
$_['text_no_relations'] = 'Немає зв’язків у БД';
|
||||||
|
$_['text_action_archive'] = 'Архівувати (в ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Архівувати + Видалити';
|
||||||
|
$_['text_tool_sanitizer'] = 'Санитайзер імен файлів';
|
||||||
|
$_['help_tool_sanitizer'] = 'Масове очищення імен файлів і папок від кирилиці, пробілів та спецсимволів, приведення розширень до єдиного вигляду.';
|
||||||
|
$_['text_description_sanitizer'] = 'Результати аналізу імен файлів для санітаризації за вибраними правилами.';
|
||||||
|
$_['entry_sz_translit'] = 'Транслітерація (кирилиця в латиницю)';
|
||||||
|
$_['entry_sz_spaces'] = 'Замінювати пробіли на символ "_"';
|
||||||
|
$_['entry_sz_special'] = 'Видаляти спеціальні символи';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Ім`я файлу в нижній регістр';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Розширення в нижній регістр';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Нормалізувати розширення (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Режим фільтрації папок';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Сканувати все, крім вибраних (Винятки)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Сканирувати тільки вибрані папки';
|
||||||
|
$_['button_select_all'] = 'Вибрати всі';
|
||||||
|
$_['button_deselect_all'] = 'Зняти всі';
|
||||||
|
$_['entry_wm_upload_font'] = 'Завантажити свій шрифт TTF';
|
||||||
|
$_['button_upload_font'] = 'Завантажити шрифт';
|
||||||
|
$_['text_success_font_upload'] = 'Шрифт успішно завантажено!';
|
||||||
|
$_['error_font_upload'] = 'Помилка завантаження шрифту! Дозволені тільки файли .ttf вагою до 5 МБ.';
|
||||||
|
$_['entry_lazy_load'] = 'Прогресивне лениве завантаження';
|
||||||
|
$_['help_lazy_load'] = 'Вмикає преміальне прогресивне відкладене завантаження на вітрині. Зображення замінюються на мікро-прев\'ю та плавно розмиваються (blur-up), підвантажуючись у міру прокручування.';
|
||||||
|
$_['confirm_replace_mode'] = 'УВАГА! Ви обрали режим «Замінювати оригінали». Усі зображення будуть перезаписані безпосередньо на сервері. Наполегливо рекомендуємо зробити резервну копію. Ви впевнені, що хочете продовжити?';
|
||||||
|
$_['text_compare'] = 'Порівняти';
|
||||||
|
$_['text_home'] = 'Головна';
|
||||||
|
$_['text_tech_gd'] = 'Бібліотека GD';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'Підтримка WebP';
|
||||||
|
$_['text_position_1'] = '1 - Зліва вгорі';
|
||||||
|
$_['text_position_2'] = '2 - По центру вгорі';
|
||||||
|
$_['text_position_3'] = '3 - Справа вгорі';
|
||||||
|
$_['text_position_4'] = '4 - Зліва посередині';
|
||||||
|
$_['text_position_5'] = '5 - По центру';
|
||||||
|
$_['text_position_6'] = '6 - Справа посередині';
|
||||||
|
$_['text_position_7'] = '7 - Зліва внизу';
|
||||||
|
$_['text_position_8'] = '8 - По центру внизу';
|
||||||
|
$_['text_position_9'] = '9 - Справа внизу';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Рівень логу';
|
||||||
|
$_['text_log_changed_only'] = 'Тільки змінені';
|
||||||
|
$_['text_log_all'] = 'Всі файли (детально)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Пропущено (без покращення)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Скинути кеш оптимізації (повне пересканування)';
|
||||||
|
$_['confirm_reset_cache'] = 'Скинути кеш оптимізації? Наступне сканування перевірить ВСІ файли знову.';
|
||||||
|
$_['text_cache_cleared'] = 'Кеш оптимізації очищено. Наступне сканування перевірить всі файли.';
|
||||||
|
$_['error_wm_preview'] = 'Помилка генерації прев\'ю водяного знаку.';
|
||||||
|
$_['error_wm_too_large'] = 'Зображення водяного знаку занадто велике (%dx%d px)! Максимально допустимий розмір: %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'УВАГА: Деякі файли не вдалося замінити (заблоковані або немає прав):';
|
||||||
|
$_['text_action_compress'] = 'Стиснути зображення';
|
||||||
|
$_['text_estimated'] = 'Прогноз';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Мін. стиснення (байт)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Мін. стиснення (%)';
|
||||||
|
$_['text_optimized'] = 'Оптимізовано';
|
||||||
|
$_['entry_broken_fallback'] = 'Заглушка для битих фото';
|
||||||
|
$_['help_broken_fallback'] = 'Автоматично замінює відсутні або пошкоджені зображення каталогу на вітрині на зображення-заглушку.';
|
||||||
|
$_['entry_fallback_image'] = 'Зображення-заглушка';
|
||||||
|
$_['help_fallback_image'] = 'Виберіть зображення, яке буде показано, якщо оригінальний файл картинки товару чи категорії відсутній. За замовчуванням використовується no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'Конвертер CMYK в RGB';
|
||||||
|
$_['help_tool_cmyk'] = 'Конвертація JPEG зображень з колірного профілю CMYK в sRGB для коректного відображення в браузерах Safari та на пристроях iOS.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Пошук важких файлів';
|
||||||
|
$_['help_tool_heavy_files'] = 'Пошук найважчих оригінальних зображень у каталозі з можливістю їх швидкого стиснення безпосередньо тут.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Макс. кількість знайдених файлів (всього)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Конвертувати в RGB';
|
||||||
|
$_['text_description_cmyk'] = 'Знайдено файли в колірному просторі CMYK. Конвертація в sRGB гарантує колірне відображення на всіх платформах, включаючи iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'Список із %d найважчих файлів у каталозі зображень. Ви можете оптимізувати їх безпосередньо тут.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Статус: Скануватимуться всі папки (винятки не обрані).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Статус: Скануватимуться всі папки, ОКРІМ %d позначених.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Статус: Скануватимуться тільки %d позначених папок.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'УВАГА: Жодної папки не обрано! Крон нічого не буде сканувати.';
|
||||||
|
$_['text_action_apply_changes'] = 'Застосувати вказані зміни';
|
||||||
|
$_['entry_wm_status'] = 'Статус водяного знака';
|
||||||
|
$_['text_disabled'] = 'Відключено';
|
||||||
|
$_['entry_rule_name'] = 'Назва правила';
|
||||||
|
$_['entry_rule_status'] = 'Статус правила';
|
||||||
|
$_['entry_action'] = 'Дія';
|
||||||
|
$_['entry_ph_status'] = 'Статус заглушок';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Макс. файлів для пошуку (ліміт)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Мінимальний розмір';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Макс. файлів для пошуку (ліміт)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Мінімальний розмір';
|
||||||
|
$_['text_wm_rule_title'] = 'Правило водяного знака';
|
||||||
|
$_['button_close'] = 'Закрити';
|
||||||
|
$_['text_ph_rule_title'] = 'Правило заглушки';
|
||||||
|
$_['entry_ph_image'] = 'Зображення заглушки';
|
||||||
|
$_['button_clear_cache_wm'] = 'Очистити кеш (застосувати водяний знак)';
|
||||||
|
$_['text_active'] = 'Активна';
|
||||||
|
$_['text_error'] = 'Не активна';
|
||||||
|
$_['text_th_dupe_delete'] = 'Файл для видалення (Дублікат)';
|
||||||
|
$_['text_th_dupe_keep'] = 'Файл для збереження (Оригінал)';
|
||||||
|
$_['text_dupe_original_label'] = 'Оригінал';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Дублікат';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Використання диска';
|
||||||
|
$_['text_cumulative_stats'] = 'Статистика оптимізації';
|
||||||
|
$_['text_disk_catalog'] = 'Каталог зображень';
|
||||||
|
$_['text_disk_cache'] = 'Кеш OC';
|
||||||
|
$_['text_disk_trash'] = 'Кошик модуля';
|
||||||
|
$_['text_disk_other'] = 'Інше';
|
||||||
|
$_['text_disk_free'] = 'Вільно';
|
||||||
|
$_['text_total_files_opt'] = 'Файлів оброблено';
|
||||||
|
$_['text_total_saved'] = 'Всього зекономлено';
|
||||||
|
$_['text_avg_saving_pct'] = 'Середня економія';
|
||||||
|
$_['text_stats_hint'] = 'Статистика накопичується по всіх сесіях (зберігається локально в браузері)';
|
||||||
|
$_['text_reset_stats'] = 'Скинути статистику';
|
||||||
|
$_['text_reset_stats_confirm']= 'Скинути накопичену статистику оптимізації?';
|
||||||
|
$_['text_export_import'] = 'Експорт / Імпорт налаштувань';
|
||||||
|
$_['button_export_settings'] = 'Експорт налаштувань (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Імпортувати налаштування';
|
||||||
|
$_['help_export_settings'] = 'Завантажити всі налаштування модуля в JSON-файл';
|
||||||
|
$_['help_import_settings'] = 'Завантажити раніше експортований JSON для відновлення налаштувань';
|
||||||
|
$_['text_import_success'] = 'Налаштування успішно імпортовані. Перезавантажте сторінку для застосування.';
|
||||||
|
$_['error_import_file'] = 'Файл не завантажено або помилка завантаження';
|
||||||
|
$_['error_import_invalid'] = 'Некоректний файл налаштувань (невірний модуль або формат)';
|
||||||
|
$_['text_click_to_load'] = 'Натисніть ↑ для завантаження даних диска';
|
||||||
|
|
||||||
|
|
||||||
|
$_['tab_formats'] = 'Формати зображень';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Ліміти диска хостингу (вручну)';
|
||||||
|
$_['text_disk_quota_desc'] = 'Якщо графік дискового простору показує некоректні дані (фізичний диск сервера замість ліміту вашого хостингу), ви можете вказати ліміти вашого тарифу вручну. Вкажіть 0, щоб використовувати автовизначення.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Виділено диска на хостингу (ГБ)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
|
||||||
|
$_['entry_auto_disk'] = 'Автооновлення при вході';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Назва задачі';
|
||||||
|
$_['entry_cron_task_summary'] = 'Параметри стиснення';
|
||||||
|
$_['button_add_cron_task'] = 'Додати задачу';
|
||||||
|
$_['button_copy_cron_url'] = 'Копіювати URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
|
||||||
|
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
|
||||||
|
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
|
||||||
|
$_['button_activate'] = 'Активувати модуль';
|
||||||
|
$_['text_support'] = 'Технічна підтримка';
|
||||||
|
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
|
||||||
|
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
|
||||||
|
$_['help_wm_max_height'] = 'Якщо вихідне зображення водяного знака перевищує цю висоту, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
|
||||||
|
$_['entry_disk_status'] = 'Увімкнути сканування диска та графік';
|
||||||
|
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
|
||||||
|
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
|
||||||
|
|
||||||
|
|
@ -0,0 +1,564 @@
|
||||||
|
<?php
|
||||||
|
$_['heading_title_raw'] = 'Image Optimizer [NAT]: Стиснення оригіналів та видалення сміття';
|
||||||
|
$_['heading_title'] = '<b>Image Optimizer [NAT]:</b> Стиснення оригіналів та видалення сміття';
|
||||||
|
$_['text_extension'] = 'Розширення';
|
||||||
|
$_['text_edit'] = 'Налаштування модуля';
|
||||||
|
$_['text_success'] = 'Налаштування успішно збережено!';
|
||||||
|
$_['entry_warning'] = 'УВАГА: Обов`язково зробіть бекап файлів сайту і особливо папки /image/catalog перед початком роботи!';
|
||||||
|
$_['text_tech'] = 'Перевірка технологій сервера:';
|
||||||
|
$_['text_author'] = 'Технічна підтримка та побажання: <a href="mailto:info@nat.od.ua">info@nat.od.ua</a>';
|
||||||
|
$_['tab_optimize'] = 'Оптимізація та Ресайз';
|
||||||
|
$_['tab_cleaner'] = 'Очищення невикористовуваних зображень';
|
||||||
|
$_['tab_info'] = 'Інфо / Екосистема [NAT]';
|
||||||
|
$_['tab_settings'] = 'Налаштування';
|
||||||
|
$_['tab_tools'] = 'Інструменти (pro)';
|
||||||
|
$_['tab_cron'] = 'Крон-завдання';
|
||||||
|
$_['entry_mode'] = 'Режим роботи';
|
||||||
|
$_['text_copy'] = 'Створити копію (безпечно)';
|
||||||
|
$_['text_replace'] = 'Замінювати оригінали (потребує бекап!)';
|
||||||
|
$_['entry_folder'] = 'Ім`я папки для копії';
|
||||||
|
$_['entry_batch'] = 'Файлів за прохід';
|
||||||
|
$_['entry_log_limit'] = 'Рядків логу на екрані';
|
||||||
|
$_['entry_max_width'] = 'Макс. ширина (px) [0 - без ліміту]';
|
||||||
|
$_['entry_max_height'] = 'Макс. висота (px) [0 - без ліміту]';
|
||||||
|
$_['entry_jpg'] = 'Якість JPG (0-100)';
|
||||||
|
$_['entry_png'] = 'Стиснення PNG (0-9)';
|
||||||
|
$_['entry_webp'] = 'Якість WebP (0-100)';
|
||||||
|
$_['entry_targets'] = 'Що оптимізувати?';
|
||||||
|
$_['text_all_catalog'] = '[ Весь каталог image/catalog ]';
|
||||||
|
$_['text_root_files'] = 'Файли тільки в корені image/catalog';
|
||||||
|
$_['entry_threshold'] = 'Поріг заміни (%)';
|
||||||
|
$_['help_threshold'] = '0: замінювати тільки якщо вага стала меншою. 10: дозволити збільшення ваги до 10%.';
|
||||||
|
$_['entry_token'] = 'Токен активації (на корінь домену)';
|
||||||
|
$_['entry_status'] = 'Статус модуля';
|
||||||
|
$_['entry_menu_position'] = 'Позиція меню [NAT]';
|
||||||
|
$_['text_menu_module_only'] = 'Тільки в модулях';
|
||||||
|
$_['text_menu_header'] = 'У шапці (Header)';
|
||||||
|
$_['text_menu_sidebar'] = 'У бічному меню (Sidebar)';
|
||||||
|
$_['text_menu_both'] = 'Скрізь (Шапка + Сайдбар)';
|
||||||
|
$_['text_status_on'] = 'Увімкнено';
|
||||||
|
$_['text_status_off'] = 'Вимкнено';
|
||||||
|
$_['entry_engine'] = 'Рушій обробки зображень';
|
||||||
|
$_['text_engine_gd'] = 'GD (Стандарт)';
|
||||||
|
$_['text_engine_imagick'] = 'Imagick (Висока якість, збереження профілів sRGB)';
|
||||||
|
$_['entry_license'] = 'Ліцензія';
|
||||||
|
$_['text_cleaner_info'] = 'Цей інструмент шукає файли, які фізично існують на диску, але ніде не використовуються в базі даних або файлах шаблону.';
|
||||||
|
$_['entry_clean_targets'] = 'В яких папках шукати сміття?';
|
||||||
|
$_['entry_extended_log'] = 'Розширений лог (показати використовувані photo і де вони знайдені)';
|
||||||
|
$_['entry_process_broken'] = 'Враховувати файли з битим кодуванням (імена з символом "?")';
|
||||||
|
$_['button_scan_trash'] = 'ЗНАЙТИ СМІТТЯ';
|
||||||
|
$_['button_quarantine'] = 'В Карантин (Безпечно)';
|
||||||
|
$_['button_delete_trash'] = 'Видалити назавжди';
|
||||||
|
$_['button_empty_quarantine'] = 'Очистити папку Карантину';
|
||||||
|
$_['button_restore_quarantine'] = 'Повернути з Карантину';
|
||||||
|
$_['text_scan_result'] = 'Результати сканування';
|
||||||
|
$_['text_trash_files'] = 'Сміттєвих файлів: ';
|
||||||
|
$_['text_trash_size'] = 'Потенційно звільниться: ';
|
||||||
|
$_['button_start'] = 'СТАРТ ОПТИМІЗАЦІЇ';
|
||||||
|
$_['button_pause'] = 'ПАУЗА';
|
||||||
|
$_['button_stop'] = 'ЗУПИНИТИ';
|
||||||
|
$_['button_continue'] = 'ПРОДОВЖИТИ';
|
||||||
|
$_['button_start_new'] = 'СТАРТ (Новий запуск)';
|
||||||
|
$_['button_download_log'] = 'Завантажити лог в TXT';
|
||||||
|
$_['button_download'] = 'Завантажити';
|
||||||
|
$_['button_save'] = 'Зберегти';
|
||||||
|
$_['button_cancel'] = 'Скасувати';
|
||||||
|
$_['text_progress'] = 'Прогрес:';
|
||||||
|
$_['text_old_size'] = 'Було:';
|
||||||
|
$_['text_new_size'] = 'Стало:';
|
||||||
|
$_['text_savings'] = 'Економія:';
|
||||||
|
$_['text_detailed_log'] = 'Детальний лог';
|
||||||
|
$_['text_page'] = 'Стор.';
|
||||||
|
$_['text_waiting'] = 'Очікування запуску...';
|
||||||
|
$_['text_enabled'] = 'Увімкнено';
|
||||||
|
$_['text_disabled_error'] = 'Вимкнено (ПОМИЛКА)';
|
||||||
|
$_['text_supported'] = 'Підтримується';
|
||||||
|
$_['text_no'] = 'Ні';
|
||||||
|
$_['error_permission'] = 'У вас немає прав для керування модулем!';
|
||||||
|
$_['error_license'] = 'Помилка доступу: Невірний токен активації для цього домену!';
|
||||||
|
$_['error_domain'] = ' (Домен: ';
|
||||||
|
$_['text_found'] = 'Знайдено файлів: ';
|
||||||
|
$_['text_compressed'] = 'Стиснуто';
|
||||||
|
$_['text_original'] = 'Оригінал';
|
||||||
|
$_['text_error_process'] = 'Помилка обробки: ';
|
||||||
|
$_['error_log_empty'] = 'Лог порожній! Спочатку запустіть процес.';
|
||||||
|
$_['text_report_header'] = '=== ЗВІТ ===';
|
||||||
|
$_['text_total_files'] = 'Всього файлів: ';
|
||||||
|
$_['text_searching'] = 'Пошук файлів...';
|
||||||
|
$_['error_ajax'] = 'Помилка: ';
|
||||||
|
$_['text_resumed'] = '▶ ПРОЦЕС ВІДНОВЛЕНО...';
|
||||||
|
$_['text_paused'] = '⏸ ПАУЗА. Процес призупинено.';
|
||||||
|
$_['confirm_stop'] = 'Ви впевнені, що хочете повністю перервати процес?';
|
||||||
|
$_['confirm_delete'] = 'УВАГА! Файли будуть видалені з сервера назавжди без можливості відновлення! Продовжити?';
|
||||||
|
$_['confirm_empty_q'] = 'Ви впевнені, що хочете повністю очистити папку карантину?';
|
||||||
|
$_['confirm_restore'] = 'Всі файли з папки карантину будуть повернуті на свої місця. Продовжити?';
|
||||||
|
$_['button_stopping'] = 'Зупинка...';
|
||||||
|
$_['text_stopped_user'] = '🛑 ПРОЦЕС ПЕРЕРВАНО КОРИСТУВАЧЕМ!';
|
||||||
|
$_['text_increase'] = 'Збільшення';
|
||||||
|
$_['text_done'] = '✅ ГОТОВО! Процес завершено.';
|
||||||
|
$_['error_timeout'] = 'Таймаут сервера. Автоповтор через 3 секунди...';
|
||||||
|
$_['text_building_index'] = 'Крок 1/2: Індексація БД та шаблонів (пошук фото, що використовуються)...';
|
||||||
|
$_['text_comparing'] = 'Крок 2/2: Порівняння фізичних файлів з індексом...';
|
||||||
|
$_['text_quarantine_done'] = '✅ Процес перенесення в image/catalog_trash/ завершено!';
|
||||||
|
$_['text_deleted_done'] = '✅ Процес видалення сміття завершено!';
|
||||||
|
$_['text_quarantine_empty'] = '✅ Папку карантину успішно видалено з сервера!';
|
||||||
|
$_['text_restore_done'] = '✅ Файли успішно повернуто з карантину в робочу папку!';
|
||||||
|
$_['text_quarantine_move'] = 'В карантині: ';
|
||||||
|
$_['text_deleted_file'] = 'Видалено: ';
|
||||||
|
$_['text_no_trash'] = 'У вибраних папках сміття не знайдено! Всі файли використовуються.';
|
||||||
|
$_['text_scanning_dirs'] = 'Скануємо директорії: ';
|
||||||
|
$_['text_log_used'] = '[ВИКОРИСТОВУЄТЬСЯ]';
|
||||||
|
$_['text_log_trash'] = '[СМІТТЯ]';
|
||||||
|
$_['text_log_deleted'] = '[ВИДАЛЕНО]';
|
||||||
|
$_['text_log_del_err'] = '[ПОМИЛКА ВИДАЛЕННЯ]';
|
||||||
|
$_['text_log_quarantine'] = '[В КАРАНТИН]';
|
||||||
|
$_['text_log_q_err'] = '[ПОМИЛКА ПЕРЕНЕСЕННЯ]';
|
||||||
|
$_['text_log_restored'] = '[ВІДНОВЛЕНО]';
|
||||||
|
$_['text_log_rest_err'] = '[ПОМИЛКА ВІДНОВЛЕННЯ]';
|
||||||
|
$_['text_used_in'] = 'у: ';
|
||||||
|
$_['text_analyzing'] = 'Аналіз бази даних...';
|
||||||
|
$_['text_clean_empty'] = 'Чисто! Сміття немає.';
|
||||||
|
$_['text_processing'] = 'Обробка...';
|
||||||
|
$_['text_restoring'] = 'Відновлення файлів...';
|
||||||
|
$_['text_all_folders'] = 'image/catalog/ (Та всі вкладені)';
|
||||||
|
$_['text_root_folder'] = 'image/catalog/ (Тільки корінь)';
|
||||||
|
$_['text_q_empty_err'] = 'Папка карантину порожня або не існує.';
|
||||||
|
$_['error_token'] = 'Невірний токен. Перезавантажте сторінку.';
|
||||||
|
$_['text_apply_title'] = 'Оптимізацію завершено в папку: ';
|
||||||
|
$_['text_apply_info'] = 'Перевірте результат. Якщо все влаштовує — застосуйте зміни до основного каталогу.';
|
||||||
|
$_['entry_apply_backup'] = 'Перенести зі створенням бекапу (в Карантин)';
|
||||||
|
$_['entry_apply_replace'] = 'Замінити оригінали безповоротно';
|
||||||
|
$_['button_apply_main'] = 'ЗАСТОСУВАТИ ДО ОСНОВНОГО КАТАЛОГУ';
|
||||||
|
$_['button_delete_copies'] = 'Видалити тимчасові копії (скасування)';
|
||||||
|
$_['button_full_cleanup'] = 'Повне очищення всіх тимчасових папок та бекапів';
|
||||||
|
$_['text_apply_success'] = '✅ Файли успішно перенесено. ';
|
||||||
|
$_['text_apply_backup_ok'] = 'Оригінали збережено в папку: ';
|
||||||
|
$_['text_apply_no_backup'] = 'Оригінали видалено.';
|
||||||
|
$_['text_error_source'] = 'Помилка: Папку з оптимізованими файлами не знайдено.';
|
||||||
|
$_['confirm_full_cleanup'] = 'УВАГА! Ця дія безповоротно видалить УСІ папки бекапів (backup_*) та тимчасові папки з копіями. Продовжити?';
|
||||||
|
$_['text_tools_desc'] = 'Глобальні сканери та інструменти для роботи з файловою системою і БД зображень.';
|
||||||
|
$_['text_tool_select'] = 'Виберіть інструмент:';
|
||||||
|
$_['text_group_scanners'] = 'Сканери та Аналіз';
|
||||||
|
$_['text_group_generators'] = 'Генератори та Обробка';
|
||||||
|
$_['text_group_experimental'] = 'Експериментальні функції (BETA)';
|
||||||
|
$_['text_experimental_warn'] = 'Увага: Експериментальні функції зачіпають системні файли або базу даних. Настійно рекомендується зробити бекап!';
|
||||||
|
$_['text_tool_broken_db'] = 'Сканер битих/порожніх фото БД';
|
||||||
|
$_['text_tool_watermark'] = 'Динамічний Вотермарк (Тільки на кеш)';
|
||||||
|
$_['text_tool_duplicates'] = 'Поиск дубликатов файлов (за MD5)';
|
||||||
|
$_['text_tool_png_jpg'] = 'Конвертер важких PNG у JPG (сканер файлів без прозорості)';
|
||||||
|
$_['text_tool_placeholders'] = 'Генератор заглушок (Замість No Image)';
|
||||||
|
$_['text_tool_exif'] = 'Очищення EXIF-даних (Геотеги/Мета)';
|
||||||
|
$_['text_tool_small_photos'] = 'Сканер занадто дрібних фото';
|
||||||
|
$_['text_tool_html_broken'] = 'Пошук битих картинок в HTML (Описах)';
|
||||||
|
$_['text_tool_empty_folders'] = 'Очищення порожніх папок (image/catalog)';
|
||||||
|
$_['text_tool_smart_cache'] = 'Розумне очищення кешу зображень';
|
||||||
|
$_['text_tool_folder_tree'] = 'Дерево папок (Статистика ваги)';
|
||||||
|
$_['text_tool_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load (Без БД)';
|
||||||
|
$_['text_tool_translit'] = 'Транслітерація назв файлів';
|
||||||
|
$_['text_tool_cache_restore'] = 'Відновлення оригіналів із Кешу';
|
||||||
|
$_['entry_wm_type'] = 'Тип водяного знака';
|
||||||
|
$_['text_wm_image'] = 'Зображення (PNG)';
|
||||||
|
$_['text_wm_text'] = 'Текст';
|
||||||
|
$_['entry_wm_image_path'] = 'Шлях до зображення';
|
||||||
|
$_['entry_wm_text_val'] = 'Текст водяного знака';
|
||||||
|
$_['entry_wm_position'] = 'Позиція (1-9)';
|
||||||
|
$_['entry_wm_opacity'] = 'Прозорість (0-100)';
|
||||||
|
$_['entry_wm_category'] = 'Тільки для Категорій';
|
||||||
|
$_['entry_wm_brand'] = 'Тільки для Брендів';
|
||||||
|
$_['entry_min_width'] = 'Мінімальна ширина (px)';
|
||||||
|
$_['entry_min_height'] = 'Мінімальна висота (px)';
|
||||||
|
$_['entry_ph_product'] = 'Заглушка для Товарів';
|
||||||
|
$_['entry_ph_category'] = 'Заглушка для Категорій';
|
||||||
|
$_['entry_ph_brand'] = 'Заглушка для Брендів';
|
||||||
|
$_['button_run_tool'] = 'Запустити інструмент';
|
||||||
|
$_['button_scan_tool'] = 'Сканувати';
|
||||||
|
$_['button_fix_tool'] = 'Виправити знайдене';
|
||||||
|
$_['text_th_path'] = 'Файл / Шлях';
|
||||||
|
$_['text_th_size'] = 'Розмір';
|
||||||
|
$_['text_th_action'] = 'Дія';
|
||||||
|
$_['text_th_status'] = 'Статус';
|
||||||
|
$_['text_th_problem'] = 'Проблема / Значення';
|
||||||
|
$_['text_cron_title'] = 'Налаштування Cron';
|
||||||
|
$_['text_cron_desc'] = 'Додайте команду в Cron хостингу для автоматичного фонового стиснення.';
|
||||||
|
$_['entry_cron_new_only'] = 'Обробляти тільки нові (необроблені) файли';
|
||||||
|
$_['entry_cron_folders'] = 'Папки для сканування (залиште порожнім для всього каталогу)';
|
||||||
|
$_['text_promo_title'] = 'Інші модулі серії [NAT]';
|
||||||
|
$_['text_promo_desc'] = 'Модулі серії [NAT] – це інструменти для глибокої оптимізації OpenCart. Ми фокусуємося на автоматизації рутини, прискоренні бази даних та очищенні серверів від сміття. Усі модулі мають відкритий вихідний код та єдину логіку управління.';
|
||||||
|
$_['text_support'] = 'Технічна підтримка та побажання:';
|
||||||
|
$_['button_more'] = 'Дивитися всі модулі на OpenCart.com';
|
||||||
|
$_['button_more_forum'] = 'Opencartforum';
|
||||||
|
$_['text_mod_cat_opt'] = 'Catalog Optimizer [NAT]';
|
||||||
|
$_['text_mod_cat_opt_desc'] = 'Приховування порожніх категорій і брендів + SEO сортування товарів (у наявності зверху) + Очищення сміття БД.';
|
||||||
|
$_['text_mod_new_arrivals'] = 'New Arrivals [NAT]: Новинки за датами';
|
||||||
|
$_['text_mod_new_arrivals_desc'] = 'Автоматична SEO-сторінка новинок. Відображає новинки суворо за реальною датою додавання. Слайдери, сітка та розумний сайдбар архіву дат.';
|
||||||
|
$_['text_mod_img_opt'] = 'Image Optimizer [NAT]';
|
||||||
|
$_['text_mod_img_opt_desc'] = '<strong>Поточний модуль.</strong> Стиснення та ресайз ОРИГІНАЛІВ фото (image/catalog) у WebP + Очищення хостингу від невикористаних зображень.';
|
||||||
|
$_['text_mod_dom_scan'] = 'Domain Scanner [NAT]';
|
||||||
|
$_['text_mod_dom_scan_desc'] = 'Пошук і чищення зовнішніх посилань і HTTP-картинок у БД та файлах теми. Ідеально для HTTPS або очищення після парсингу.';
|
||||||
|
$_['text_mod_poll'] = 'Poll Widget [NAT]';
|
||||||
|
$_['text_mod_poll_desc'] = 'Створення опитувань та голосувань із візуальним конструктором дизайну, глибокою статистикою та захистом від накруток.';
|
||||||
|
$_['text_mod_db_opt'] = 'Database Optimizer [NAT]';
|
||||||
|
$_['text_mod_db_opt_desc'] = 'Інтелектуальне очищення сміття в базі даних, управління системними логами та додавання критично важливих індексів для прискорення.';
|
||||||
|
$_['text_mod_im_ex'] = 'Import/Export [NAT]';
|
||||||
|
$_['text_mod_im_ex_desc'] = 'Швидкий імпорт/експорт цін і залишків з файлів xls/xlsx/csv/xml/json або за посиланням. Маппінг полів, робота по Cron, текстові функції.';
|
||||||
|
$_['text_mod_admin_tools'] = 'Admin Tools [NAT]';
|
||||||
|
$_['text_mod_admin_tools_desc'] = 'Зручні інструменти адміністратора: вхід під клієнтом, кастомізація адмінки, управління правами користувачів та приховування непотрібних пунктів меню.';
|
||||||
|
$_['text_mod_utm_tracker'] = 'Order UTM Tracker [NAT]';
|
||||||
|
$_['text_mod_utm_tracker_desc'] = 'Автоматичне відстеження UTM-міток при оформленні замовлення. Зберігає джерело переходу, кампанію, ключові слова та виводить ці дані в замовленні для аналітики реклами.';
|
||||||
|
$_['text_info_title_promo_top_bar'] = 'Promo Top Bar [NAT]: Верхній рекламний інфо-баннер, інформаційна смуга над шапкою, рухомий рядок та топ-баннер';
|
||||||
|
$_['text_info_desc_promo_top_bar'] = 'Створення інтерактивних промо-панелей та слайд-шоу в шапці сайту з конструктором блоків, таймерами, формою підписки та гнучким націлюванням.';
|
||||||
|
$_['text_tool_no_image'] = 'Товари без головного фото (порожньо в БД)';
|
||||||
|
$_['text_problem_main_broken'] = 'Бите головне фото (файл видалено)';
|
||||||
|
$_['text_problem_add_broken'] = 'Бите дод. фото (файл видалено)';
|
||||||
|
$_['text_problem_no_image'] = 'Відсутнє головне фото';
|
||||||
|
$_['text_err_dir'] = 'Помилка: Директорію не знайдено або невірний шлях.';
|
||||||
|
$_['text_err_no_queue'] = 'Помилка: Чергу не знайдено. Спочатку запустіть сканування.';
|
||||||
|
$_['text_log_del_empty_folder'] = 'Видалено порожню папку: ';
|
||||||
|
$_['text_log_no_empty_folders'] = 'Порожніх папок в image/catalog/ не знайдено.';
|
||||||
|
$_['text_log_total_empty_folders'] = 'Всього видалено порожніх папок: ';
|
||||||
|
$_['text_log_cache_cleared'] = 'Кеш зображень успішно очищено.';
|
||||||
|
$_['text_log_wm_smart_mode'] = 'Вотермарки працюють в SMART-режимі: оригінали не зачіпаються, логотип накладається лише при генерації кешу.';
|
||||||
|
$_['text_log_scan_dupes'] = 'Сканування image/catalog/ на дублікати (за хешем MD5)... Це може зайняти час.';
|
||||||
|
$_['text_log_found_dupe'] = 'Знайдено дублікат: ';
|
||||||
|
$_['text_log_no_dupes'] = 'Дублікатів не знайдено.';
|
||||||
|
$_['text_log_total_dupes'] = 'Знайдено дублікатів: %s. Потенційна економія: %s';
|
||||||
|
$_['text_log_fix_dupes_prompt'] = 'Натисніть "Виправити знайдене" для перелінкування БД на оригинал та видалення дублів.';
|
||||||
|
$_['text_log_fixed_dupe'] = 'Виправлено та видалено дублікат: ';
|
||||||
|
$_['text_log_success_dupes'] = 'Успішно виправлено дублікатів: %s. Посилання в БД оновлено.';
|
||||||
|
$_['text_log_scan_png'] = 'Пошук важких PNG файлів без прозорості...';
|
||||||
|
$_['text_log_no_png'] = 'Відповідних PNG файлів не знайдено.';
|
||||||
|
$_['text_log_total_png'] = 'Знайдено PNG без прозорості: %s. Натисніть "Виправити" для конвертації в JPG.';
|
||||||
|
$_['text_log_converted_jpg'] = 'Сконвертовано в JPG: ';
|
||||||
|
$_['text_log_success_png'] = 'Успішно сконвертовано %s файлів. Звільнено: %s';
|
||||||
|
$_['text_log_ph_prod'] = 'Оновлено товарів заглушкою: ';
|
||||||
|
$_['text_log_ph_cat'] = 'Оновлено категорій заглушкою: ';
|
||||||
|
$_['text_log_ph_brand'] = 'Оновлено брендів заглушкою: ';
|
||||||
|
$_['text_log_ph_empty'] = 'Заглушки не вибрані в налаштуваннях.';
|
||||||
|
$_['text_log_scan_exif'] = 'Сканування EXIF-даних (мета-теги камер)...';
|
||||||
|
$_['text_log_stripped_exif'] = 'Очищено EXIF: ';
|
||||||
|
$_['text_log_no_exif'] = 'Файлів з EXIF для очищення не знайдено.';
|
||||||
|
$_['text_log_success_exif'] = 'Очищено EXIF у %s файлів. Звільнено: %s';
|
||||||
|
$_['text_log_scan_small'] = 'Пошук фото розміром менше %sx%s px...';
|
||||||
|
$_['text_log_too_small'] = 'Занадто мале (%sx%s): ';
|
||||||
|
$_['text_log_no_small'] = 'Дрібних фото не знайдено.';
|
||||||
|
$_['text_log_total_small'] = 'Знайдено дрібних фото: %s. Будь ласка, замініть їх на якісні.';
|
||||||
|
$_['text_log_html_broken'] = 'Битией IMG в HTML таблиці %s (ID: %s): ';
|
||||||
|
$_['text_log_no_html_broken'] = 'Битих картинок в HTML-описах не знайдено.';
|
||||||
|
$_['text_log_total_html_broken'] = 'Знайдено битих посилань в HTML: %s. Виправте їх вручну.';
|
||||||
|
$_['text_log_scan_smart_cache'] = 'Пошук втрачених файлів у image/cache/...';
|
||||||
|
$_['text_log_del_orph_cache'] = 'Видалено втрачений кеш: ';
|
||||||
|
$_['text_log_no_orph_cache'] = 'Втраченого кешу не знайдено.';
|
||||||
|
$_['text_log_success_smart_cache'] = 'Видалено файлів втраченого кешу: %s. Звільнено: %s';
|
||||||
|
$_['text_log_scan_translit'] = 'Пошук файлів з кирилицею або пробілами...';
|
||||||
|
$_['text_log_no_bad_names'] = 'Файлів з некоректними іменами не знайдено.';
|
||||||
|
$_['text_log_total_bad_names'] = 'Знайдено файлів з кирилицею/пробілами: %s. Натисніть "Виправити" для транслітерації та оновлення БД.';
|
||||||
|
$_['text_log_renamed'] = 'Перейменовано: ';
|
||||||
|
$_['text_log_success_translit'] = 'Успішно транслітеровано файлів: %s. БД оновлено.';
|
||||||
|
$_['text_log_scan_restore'] = 'Сканирование image/cache/ для відновлення втрачених оригіналів...';
|
||||||
|
$_['text_log_restored_cache'] = 'Відновлено з кешу: ';
|
||||||
|
$_['text_log_no_lost_orig'] = 'Відсутніх оригіналів у кеші не знайдено.';
|
||||||
|
$_['text_log_success_restore'] = 'Успішно відновлено оригіналів з кешу: %s.';
|
||||||
|
$_['error_invalid_folder'] = 'Помилка: Неприпустиме ім\'я папки!';
|
||||||
|
$_['text_formats_title'] = 'Сучасні формати (WebP & SVG)';
|
||||||
|
$_['text_formats_desc'] = 'Налаштування інтеграції сучасних форматів у файлову систему та шаблони OpenCart.';
|
||||||
|
$_['entry_support_svg'] = 'Підтримка завантаження SVG';
|
||||||
|
$_['help_support_svg'] = 'Дозволяє завантаження векторних SVG-зображень через стандартний Filemanager та їх безпечне виведення на вітрині без спотворення розмірів.';
|
||||||
|
$_['entry_support_webp'] = 'Підтримка завантаження WebP';
|
||||||
|
$_['help_support_webp'] = 'Дозволяє вручну завантажувати, підв\'язувати та виводити на сайті готові файли .webp через Filemanager.';
|
||||||
|
$_['entry_truefilemanager_webp'] = 'WebP у TrueFileManager';
|
||||||
|
$_['help_truefilemanager_webp'] = 'Додає підтримку завантаження, попереднього перегляду та ескізів WebP у TrueFileManager від sitecreator (якщо його встановлено).';
|
||||||
|
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
|
||||||
|
$_['help_webp_on_fly'] = 'Надшвидка підміна: автоматично змінює розширення .jpg/.png на .webp прямо в HTML-виводі вітрини та додає атрибут loading="lazy". Не чіпає базу даних!';
|
||||||
|
$_['help_tool_broken_db'] = 'Шукає в базі даних зображення, фізичні файли яких були видалені з сервера. Дозволяє очистити биті посилання.';
|
||||||
|
$_['help_tool_empty_folders'] = 'Рекурсивно сканує директорію image/catalog/ та безпечно видаляє всі папки, в яких немає жодного файлу.';
|
||||||
|
$_['help_tool_folder_tree'] = 'Будує дерево каталогів та показує реальну вагу кожної папки для пошуку "пожирачів" місця.';
|
||||||
|
$_['help_tool_html_broken'] = 'Сканує HTML-описи товарів та категорій на наявність тегів <img> з неіснуючими зображеннями.';
|
||||||
|
$_['help_tool_small_photos'] = 'Знаходить зображення з роздільною здатністю нижче заданої. Додано посилання на пошук якісних аналогів у Google Images та Яндекс Зображеннях.';
|
||||||
|
$_['help_tool_duplicates'] = 'Шукає повні дублікати файлів за MD5-хешем (різні назви, але однакова картинка). Дозволяє склеїти їх в один файл та оновити БД.';
|
||||||
|
$_['help_tool_watermark'] = 'Накладення водяних знаків у SMART-режимі (тільки на кеш вітрини, оригінали не змінюються).';
|
||||||
|
$_['help_tool_placeholders'] = 'Масово встановлює вибране зображення-заглушку для товарів та категорій, у яких взагалі немає фото.';
|
||||||
|
$_['help_tool_png_jpg'] = 'Інструмент сканує папку зображень і знаходить важкі PNG-файли, які не мають прозорого фону (альфа-каналу). Оскільки прозорість їм не потрібна, їх можна безпечно конвертувати у формат JPG. Це значно полегшує файли (в середньому на 70-80%) та прискорює завантаження сайту, а всі посилання в базі даних автоматично оновлюються.';
|
||||||
|
$_['help_tool_exif'] = 'Сканує EXIF-дані та видаляє з них приховані теги, зменшуючи вагу на 10-15%.';
|
||||||
|
$_['help_tool_smart_cache'] = 'Шукає в папці image/cache/ кешовані файли, оригінали яких уже видалені, та очищає неактуальний кеш.';
|
||||||
|
$_['help_tool_translit'] = 'Знаходить файли з кирилицею або пробілами, перейменовує їх транслітом та оновлює шляхи в БД.';
|
||||||
|
$_['help_tool_cache_restore'] = 'Якщо ви випадково видалили оригінал з catalog, інструмент спробує знайти його копію в cache та відновити назад.';
|
||||||
|
$_['entry_quarantine'] = 'Перемістити оригінали в Карантин (замість видалення)';
|
||||||
|
$_['entry_backup_db'] = 'Створити бекап змінюваних таблиць у SQL перед запуском';
|
||||||
|
$_['button_fix_selected'] = 'ВИПРАВИТИ ВИБРАНІ';
|
||||||
|
$_['button_export_log'] = 'Експортувати в CSV';
|
||||||
|
$_['text_th_before'] = 'Стан ДО';
|
||||||
|
$_['text_th_after'] = 'Очікуваний результат ПІСЛЯ';
|
||||||
|
$_['text_th_select'] = 'Дія';
|
||||||
|
$_['text_th_preview'] = 'Передперегляд';
|
||||||
|
$_['text_th_name'] = 'Об`єкт / Файл';
|
||||||
|
$_['text_global_action'] = 'Групова дія для вибраних:';
|
||||||
|
$_['text_action_clear'] = 'Очистити посилання в БД';
|
||||||
|
$_['text_action_disable'] = 'Вимкнути (Статус = 0)';
|
||||||
|
$_['text_action_stock'] = 'Установити "Немає в наявності"';
|
||||||
|
$_['text_action_placeholder'] = 'Встановити заглушку';
|
||||||
|
$_['text_action_ignore'] = 'Нічого не робити';
|
||||||
|
$_['text_action_merge'] = 'Склеїти (видалити дублікат, залишити оригінал)';
|
||||||
|
$_['text_action_convert'] = 'Конвертувати в JPG';
|
||||||
|
$_['text_action_rename'] = 'Перейменувати транслітом';
|
||||||
|
$_['text_action_delete'] = 'Видалити';
|
||||||
|
$_['text_action_restore'] = 'Відновити';
|
||||||
|
$_['text_db_backup_created'] = 'Створено бекап БД: %s';
|
||||||
|
$_['text_quarantine_created'] = 'Перенесено в карантин: %s';
|
||||||
|
$_['text_fix_status_success'] = 'Успішно виконано';
|
||||||
|
$_['text_fix_status_error'] = 'Помилка: %s';
|
||||||
|
$_['text_log_exported'] = 'Лог успішно експортовано!';
|
||||||
|
$_['text_select_action'] = '-- Виберіть дію --';
|
||||||
|
$_['error_empty_selection'] = 'Помилка: Не вибрано жодного рядка для виправлення!';
|
||||||
|
$_['text_link_admin'] = 'Адмінка';
|
||||||
|
$_['text_link_catalog'] = 'Вітрина';
|
||||||
|
$_['entry_threads'] = 'Потоків обробки';
|
||||||
|
$_['text_threads_optimal'] = 'оптимально';
|
||||||
|
$_['button_download_zip'] = 'Завантажити ZIP-архів та видалити папку';
|
||||||
|
$_['confirm_download_and_delete'] = 'Архів успішно завантажився? Можна видалити тимчасову папку копій на сервері?';
|
||||||
|
$_['button_clear_backups'] = 'Видалити бекапи БД';
|
||||||
|
$_['button_clear_backups_short'] = 'Очистити бекапи';
|
||||||
|
$_['confirm_clear_backups'] = 'Ви впевнені, що хочете повністю видалити всі SQL-бекапи таблиць?';
|
||||||
|
$_['text_compare_title'] = 'Порівняння зображень ДО / ПІСЛЯ';
|
||||||
|
$_['text_compare_before'] = 'ДО';
|
||||||
|
$_['text_compare_after'] = 'ПІСЛЯ';
|
||||||
|
$_['text_backups_cleared'] = 'Усі SQL-бекапи таблиць успішно видалені!';
|
||||||
|
$_['error_no_backups'] = 'Бекапи не знайдені або папка порожня.';
|
||||||
|
$_['button_restore'] = 'Повернути';
|
||||||
|
$_['button_empty'] = 'Очистити';
|
||||||
|
$_['entry_wm_targets'] = 'Застосовувати водяний знак до:';
|
||||||
|
$_['text_wm_target_product'] = 'Фото товарів';
|
||||||
|
$_['text_wm_target_category'] = 'Фото категорій';
|
||||||
|
$_['text_wm_target_brand'] = 'Логотипи брендів';
|
||||||
|
$_['text_wm_target_banner'] = 'Банери та слайди';
|
||||||
|
$_['text_wm_target_blog'] = 'Статті / Блог';
|
||||||
|
$_['error_no_scan_data'] = 'Помилка: Дані сканування не знайдені. Спочатку запустіть сканування!';
|
||||||
|
$_['error_row_not_found'] = 'Помилка: Об`єкт не знайдено в результатах сканування!';
|
||||||
|
$_['error_file_not_found'] = 'Помилка: Файл не знайдено на сервері!';
|
||||||
|
$_['text_folder_rules_title'] = 'Індивідуальні правила стиснення для папок';
|
||||||
|
$_['text_folder_rules_desc'] = 'Ви можете перевизначити якість та максимальні розміри для конкретних папок. Якщо залишити поле порожнім, використовуватиметься глобальне налаштування. Шлях вказується відносно папки image (наприклад, catalog/banners/).';
|
||||||
|
$_['entry_folder_path'] = 'Шлях до папки';
|
||||||
|
$_['entry_jpg_quality'] = 'Якість JPG';
|
||||||
|
$_['entry_webp_quality'] = 'Якість WebP';
|
||||||
|
$_['button_add_rule'] = 'Додати правило';
|
||||||
|
$_['button_remove'] = 'Вилучити';
|
||||||
|
$_['entry_wm_angle'] = 'Кут нахилу водяного знака (градуси)';
|
||||||
|
$_['entry_wm_corner_radius'] = 'Закруглення кутів водяного знака (px)';
|
||||||
|
$_['entry_wm_size_type'] = 'Масштабування водяного знака';
|
||||||
|
$_['entry_wm_size_percent'] = 'Розмір водяного знака (% від ширини зображення)';
|
||||||
|
$_['entry_wm_text_color'] = 'Колір тексту водяного знака';
|
||||||
|
$_['entry_wm_text_font'] = 'Шрифт водяного знака (TTF)';
|
||||||
|
$_['entry_wm_filter_mode'] = 'Режим фільтрації Категорій/Брендів';
|
||||||
|
$_['text_wm_filter_include'] = 'Тільки для вибраних';
|
||||||
|
$_['text_wm_filter_exclude'] = 'Для всіх, крім вибраних';
|
||||||
|
$_['text_wm_size_original'] = 'Оригінальний розмір';
|
||||||
|
$_['text_wm_size_percent'] = 'Пропорційно ширині зображення (%)';
|
||||||
|
$_['button_preview_watermark'] = 'Попередній перегляд';
|
||||||
|
$_['text_wm_preview_title'] = 'Попередній перегляд водяного знака';
|
||||||
|
$_['text_cron_exclude_folders'] = 'Виключити папки зі сканування';
|
||||||
|
$_['text_cron_entities'] = 'Які типи фото обробляти?';
|
||||||
|
$_['text_cron_quality_override'] = 'Перевизначити параметри стиснення для Крона';
|
||||||
|
$_['text_cron_recommendations'] = 'Рекомендації щодо розкладу Cron';
|
||||||
|
$_['text_cron_recommendations_desc'] = '<ul><li><b>Для невеликих сайтів (до 5000 товарів)</b> рекомендується запускати Крон кожну годину (<code>0 * * * *</code>).</li><li><b>Для середніх сайтів (5000-20000 товарів)</b> рекомендується запускати раз на 3-6 годин (<code>0 */6 * * *</code>).</li><li><b>Для великих сайтів (понад 20000 товарів)</b> — один раз на добу вночі (наприклад, о 2:00: <code>0 2 * * *</code>).</li></ul>';
|
||||||
|
$_['text_tool_broken_files'] = 'Сканер битих/порожніх файлів на сервері';
|
||||||
|
$_['help_tool_broken_files'] = 'Сканує фізичні файли в папці image/catalog/ на наявність пошкоджених зображень або порожніх файлів розміром 0 байт.';
|
||||||
|
$_['text_grid_broken_file'] = 'Битий або порожній файл на сервері';
|
||||||
|
$_['text_grid_broken_file_after'] = 'Вилучення або заміна заглушкою';
|
||||||
|
$_['text_description_broken_files'] = 'Результати сканування фізичних файлів у папці image/catalog/. Тут відображаються биті (пошкоджені) зображення або файли розміром 0 байт.';
|
||||||
|
$_['text_action_strip'] = 'Очистити EXIF';
|
||||||
|
$_['text_action_set_placeholder'] = 'Встановити заглушку';
|
||||||
|
$_['text_th_entity'] = 'Об`єкт / Пов`язані посилання';
|
||||||
|
$_['text_cron_entity_product'] = 'Товари';
|
||||||
|
$_['text_cron_entity_category'] = 'Категорії';
|
||||||
|
$_['text_cron_entity_manufacturer'] = 'Виробники / Бренди';
|
||||||
|
$_['text_cron_entity_banner'] = 'Банери';
|
||||||
|
$_['text_cron_entity_blog'] = 'Блог / Статті';
|
||||||
|
$_['entry_cron_jpg_quality'] = 'Якість JPG (Крон)';
|
||||||
|
$_['entry_cron_webp_quality'] = 'Якість WebP (Крон)';
|
||||||
|
$_['entry_cron_max_width'] = 'Макс. ширина px (Крон)';
|
||||||
|
$_['entry_cron_max_height'] = 'Макс. висота px (Крон)';
|
||||||
|
$_['text_cron_folder_scan'] = 'Папки для сканування';
|
||||||
|
$_['text_no_fonts'] = 'TTF-шрифти не знайдено в system/library/font/';
|
||||||
|
$_['button_apply'] = 'Застосувати';
|
||||||
|
$_['text_backup_will_be_created'] = 'Резервна копія бази даних буде автоматично створена перед виконанням цієї дії.';
|
||||||
|
$_['text_bulk_action'] = 'Групова дія для вибраних';
|
||||||
|
$_['text_grid_results'] = 'Результати та деталі сканування';
|
||||||
|
$_['text_quarantine_will_be_created'] = 'Оригінальні файли будуть переміщені в карантин (image/catalog_trash/) перед зміною.';
|
||||||
|
$_['text_success_apply'] = 'Зміни успішно застосовані!';
|
||||||
|
$_['text_description_broken'] = 'Результати сканування бази даних на наявність битих посилань (записи в таблицях з неіснуючими файлами).';
|
||||||
|
$_['text_description_cache_restore'] = 'Результати пошуку файлів у кеші, оригінали яких відсутні в каталозі.';
|
||||||
|
$_['text_description_duplicates'] = 'Результати пошуку повних дублікатів зображень за MD5-хешем.';
|
||||||
|
$_['text_description_empty_folders'] = 'Список порожніх папок в директорії image/catalog/ для видалення.';
|
||||||
|
$_['text_description_exif'] = 'Результати пошуку зображень з метаданими EXIF, які можна очистити.';
|
||||||
|
$_['text_description_folder_tree'] = 'Статистика розміру папок у каталозі image/catalog/.';
|
||||||
|
$_['text_description_html_broken'] = 'Результати пошуку неіснуючих файлів картинок, на які посилаються описи в HTML.';
|
||||||
|
$_['text_description_placeholders'] = 'Результати пошуку сутностей без зображень для заповнення заглушками.';
|
||||||
|
$_['text_description_png_jpg'] = 'Результати пошуку важких зображень PNG без прозорості, які можна перетиснути в JPG.';
|
||||||
|
$_['text_description_small_photos'] = 'Список зображень, розміри яких менше мінімально заданих (%sx%s px).';
|
||||||
|
$_['text_description_smart_cache'] = 'Результати пошуку файлів кешу, оригінали яких уже було видалено.';
|
||||||
|
$_['text_description_translit'] = 'Результати пошуку файлів з кирилицею або пробілами в назвах.';
|
||||||
|
$_['text_grid_broken_db_after'] = 'Очистити посилання або встановити заглушку';
|
||||||
|
$_['text_grid_dupe_after'] = 'Оригінал (залишається)';
|
||||||
|
$_['text_grid_dupe_before'] = 'Дублікат (буде видалено)';
|
||||||
|
$_['text_grid_empty_folder'] = 'Порожня папка';
|
||||||
|
$_['text_grid_empty_folder_after'] = 'Вилучити порожню папку';
|
||||||
|
$_['text_grid_exif_after'] = 'Очистити метадані';
|
||||||
|
$_['text_grid_exif_before'] = 'Містить EXIF';
|
||||||
|
$_['text_grid_html_after'] = 'Виправити посилання вручну';
|
||||||
|
$_['text_grid_missing_file'] = 'Файл відсутній';
|
||||||
|
$_['text_grid_missing_html'] = 'Картинка не знайдена в описі';
|
||||||
|
$_['text_grid_missing_orig'] = 'Оригінал відсутній';
|
||||||
|
$_['text_grid_missing_orig_after'] = 'Відновити оригінал з кешу';
|
||||||
|
$_['text_grid_no_image'] = 'Немає зображення (порожньо)';
|
||||||
|
$_['text_grid_orph_cache'] = 'Осиротілий кеш';
|
||||||
|
$_['text_grid_orph_cache_after'] = 'Видалити непотрібний кеш';
|
||||||
|
$_['text_grid_ph_brand_after'] = 'Встановити заглушку бренду';
|
||||||
|
$_['text_grid_ph_category_after'] = 'Встановити заглушку категорії';
|
||||||
|
$_['text_grid_ph_product_after'] = 'Встановити заглушку товару';
|
||||||
|
$_['text_grid_small_photo_after'] = 'Рекомендується замінити на більше';
|
||||||
|
$_['text_grid_too_small'] = 'Занадто мале';
|
||||||
|
$_['text_no_relations'] = 'Немає зв’язків у БД';
|
||||||
|
$_['text_action_archive'] = 'Архівувати (в ZIP)';
|
||||||
|
$_['text_action_archive_delete'] = 'Архівувати + Видалити';
|
||||||
|
$_['text_tool_sanitizer'] = 'Санитайзер імен файлів';
|
||||||
|
$_['help_tool_sanitizer'] = 'Масове очищення імен файлів і папок від кирилиці, пробілів та спецсимволів, приведення розширень до єдиного вигляду.';
|
||||||
|
$_['text_description_sanitizer'] = 'Результати аналізу імен файлів для санітаризації за вибраними правилами.';
|
||||||
|
$_['entry_sz_translit'] = 'Транслітерація (кирилиця в латиницю)';
|
||||||
|
$_['entry_sz_spaces'] = 'Замінювати пробіли на символ "_"';
|
||||||
|
$_['entry_sz_special'] = 'Видаляти спеціальні символи';
|
||||||
|
$_['entry_sz_lowercase_name'] = 'Ім`я файлу в нижній регістр';
|
||||||
|
$_['entry_sz_lowercase_ext'] = 'Розширення в нижній регістр';
|
||||||
|
$_['entry_sz_normalize_ext'] = 'Нормалізувати розширення (jpeg -> jpg)';
|
||||||
|
$_['entry_cron_folder_mode'] = 'Режим фільтрації папок';
|
||||||
|
$_['text_cron_folder_mode_exclude'] = 'Сканувати все, крім вибраних (Винятки)';
|
||||||
|
$_['text_cron_folder_mode_include'] = 'Сканирувати тільки вибрані папки';
|
||||||
|
$_['button_select_all'] = 'Вибрати всі';
|
||||||
|
$_['button_deselect_all'] = 'Зняти всі';
|
||||||
|
$_['entry_wm_upload_font'] = 'Завантажити свій шрифт TTF';
|
||||||
|
$_['button_upload_font'] = 'Завантажити шрифт';
|
||||||
|
$_['text_success_font_upload'] = 'Шрифт успішно завантажено!';
|
||||||
|
$_['error_font_upload'] = 'Помилка завантаження шрифту! Дозволені тільки файли .ttf вагою до 5 МБ.';
|
||||||
|
$_['entry_lazy_load'] = 'Прогресивне лениве завантаження';
|
||||||
|
$_['help_lazy_load'] = 'Вмикає преміальне прогресивне відкладене завантаження на вітрині. Зображення замінюються на мікро-прев\'ю та плавно розмиваються (blur-up), підвантажуючись у міру прокручування.';
|
||||||
|
$_['confirm_replace_mode'] = 'УВАГА! Ви обрали режим «Замінювати оригінали». Усі зображення будуть перезаписані безпосередньо на сервері. Наполегливо рекомендуємо зробити резервну копію. Ви впевнені, що хочете продовжити?';
|
||||||
|
$_['text_compare'] = 'Порівняти';
|
||||||
|
$_['text_home'] = 'Головна';
|
||||||
|
$_['text_tech_gd'] = 'Бібліотека GD';
|
||||||
|
$_['text_tech_imagick'] = 'Imagick';
|
||||||
|
$_['text_tech_webp'] = 'Підтримка WebP';
|
||||||
|
$_['text_position_1'] = '1 - Зліва вгорі';
|
||||||
|
$_['text_position_2'] = '2 - По центру вгорі';
|
||||||
|
$_['text_position_3'] = '3 - Справа вгорі';
|
||||||
|
$_['text_position_4'] = '4 - Зліва посередині';
|
||||||
|
$_['text_position_5'] = '5 - По центру';
|
||||||
|
$_['text_position_6'] = '6 - Справа посередині';
|
||||||
|
$_['text_position_7'] = '7 - Зліва внизу';
|
||||||
|
$_['text_position_8'] = '8 - По центру внизу';
|
||||||
|
$_['text_position_9'] = '9 - Справа внизу';
|
||||||
|
|
||||||
|
$_['entry_log_level'] = 'Рівень логу';
|
||||||
|
$_['text_log_changed_only'] = 'Тільки змінені';
|
||||||
|
$_['text_log_all'] = 'Всі файли (детально)';
|
||||||
|
$_['text_skipped_no_gain'] = 'Пропущено (без покращення)';
|
||||||
|
$_['text_reset_cache_hint'] = 'Скинути кеш оптимізації (повне пересканування)';
|
||||||
|
$_['confirm_reset_cache'] = 'Скинути кеш оптимізації? Наступне сканування перевірить ВСІ файли знову.';
|
||||||
|
$_['text_cache_cleared'] = 'Кеш оптимізації очищено. Наступне сканування перевірить всі файли.';
|
||||||
|
$_['error_wm_preview'] = 'Помилка генерації прев\'ю водяного знаку.';
|
||||||
|
$_['error_wm_too_large'] = 'Зображення водяного знаку занадто велике (%dx%d px)! Максимально допустимий розмір: %dx%d px.';
|
||||||
|
$_['text_apply_errors_warning'] = 'УВАГА: Деякі файли не вдалося замінити (заблоковані або немає прав):';
|
||||||
|
$_['text_action_compress'] = 'Стиснути зображення';
|
||||||
|
$_['text_estimated'] = 'Прогноз';
|
||||||
|
$_['entry_min_savings_bytes'] = 'Мін. стиснення (байт)';
|
||||||
|
$_['entry_min_savings_percent'] = 'Мін. стиснення (%)';
|
||||||
|
$_['text_optimized'] = 'Оптимізовано';
|
||||||
|
$_['entry_broken_fallback'] = 'Заглушка для битих фото';
|
||||||
|
$_['help_broken_fallback'] = 'Автоматично замінює відсутні або пошкоджені зображення каталогу на вітрині на зображення-заглушку.';
|
||||||
|
$_['entry_fallback_image'] = 'Зображення-заглушка';
|
||||||
|
$_['help_fallback_image'] = 'Виберіть зображення, яке буде показано, якщо оригінальний файл картинки товару чи категорії відсутній. За замовчуванням використовується no_image.png.';
|
||||||
|
$_['text_tool_cmyk'] = 'Конвертер CMYK в RGB';
|
||||||
|
$_['help_tool_cmyk'] = 'Конвертація JPEG зображень з колірного профілю CMYK в sRGB для коректного відображення в браузерах Safari та на пристроях iOS.';
|
||||||
|
$_['text_tool_heavy_files'] = 'Пошук важких файлів';
|
||||||
|
$_['help_tool_heavy_files'] = 'Пошук найважчих оригінальних зображень у каталозі з можливістю їх швидкого стиснення безпосередньо тут.';
|
||||||
|
$_['entry_heavy_files_limit'] = 'Макс. кількість знайдених файлів (всього)';
|
||||||
|
$_['text_action_convert_rgb'] = 'Конвертувати в RGB';
|
||||||
|
$_['text_description_cmyk'] = 'Знайдено файли в колірному просторі CMYK. Конвертація в sRGB гарантує колірне відображення на всіх платформах, включаючи iOS/macOS.';
|
||||||
|
$_['text_description_heavy'] = 'Список із %d найважчих файлів у каталозі зображень. Ви можете оптимізувати їх безпосередньо тут.';
|
||||||
|
$_['text_cron_folder_hint_exclude_all'] = 'Статус: Скануватимуться всі папки (винятки не обрані).';
|
||||||
|
$_['text_cron_folder_hint_exclude_some'] = 'Статус: Скануватимуться всі папки, ОКРІМ %d позначених.';
|
||||||
|
$_['text_cron_folder_hint_include_some'] = 'Статус: Скануватимуться тільки %d позначених папок.';
|
||||||
|
$_['text_cron_folder_hint_include_none'] = 'УВАГА: Жодної папки не обрано! Крон нічого не буде сканувати.';
|
||||||
|
$_['text_action_apply_changes'] = 'Застосувати вказані зміни';
|
||||||
|
$_['entry_wm_status'] = 'Статус водяного знака';
|
||||||
|
$_['text_disabled'] = 'Відключено';
|
||||||
|
$_['entry_rule_name'] = 'Назва правила';
|
||||||
|
$_['entry_rule_status'] = 'Статус правила';
|
||||||
|
$_['entry_action'] = 'Дія';
|
||||||
|
$_['entry_ph_status'] = 'Статус заглушок';
|
||||||
|
$_['entry_sanitizer_limit'] = 'Макс. файлів для пошуку (ліміт)';
|
||||||
|
$_['entry_heavy_files_min_size'] = 'Мінімальний розмір';
|
||||||
|
$_['entry_png_jpg_limit'] = 'Макс. файлів для пошуку (ліміт)';
|
||||||
|
$_['entry_png_jpg_min_size'] = 'Мінімальний розмір';
|
||||||
|
$_['text_wm_rule_title'] = 'Правило водяного знака';
|
||||||
|
$_['button_close'] = 'Закрити';
|
||||||
|
$_['text_ph_rule_title'] = 'Правило заглушки';
|
||||||
|
$_['entry_ph_image'] = 'Зображення заглушки';
|
||||||
|
$_['button_clear_cache_wm'] = 'Очистити кеш (застосувати водяний знак)';
|
||||||
|
$_['text_active'] = 'Активна';
|
||||||
|
$_['text_error'] = 'Не активна';
|
||||||
|
$_['text_th_dupe_delete'] = 'Файл для видалення (Дублікат)';
|
||||||
|
$_['text_th_dupe_keep'] = 'Файл для збереження (Оригінал)';
|
||||||
|
$_['text_dupe_original_label'] = 'Оригінал';
|
||||||
|
$_['text_dupe_duplicate_label'] = 'Дублікат';
|
||||||
|
|
||||||
|
$_['text_disk_usage'] = 'Використання диска';
|
||||||
|
$_['text_cumulative_stats'] = 'Статистика оптимізації';
|
||||||
|
$_['text_disk_catalog'] = 'Каталог зображень';
|
||||||
|
$_['text_disk_cache'] = 'Кеш OC';
|
||||||
|
$_['text_disk_trash'] = 'Кошик модуля';
|
||||||
|
$_['text_disk_other'] = 'Інше';
|
||||||
|
$_['text_disk_free'] = 'Вільно';
|
||||||
|
$_['text_total_files_opt'] = 'Файлів оброблено';
|
||||||
|
$_['text_total_saved'] = 'Всього зекономлено';
|
||||||
|
$_['text_avg_saving_pct'] = 'Середня економія';
|
||||||
|
$_['text_stats_hint'] = 'Статистика накопичується по всіх сесіях (зберігається локально в браузері)';
|
||||||
|
$_['text_reset_stats'] = 'Скинути статистику';
|
||||||
|
$_['text_reset_stats_confirm']= 'Скинути накопичену статистику оптимізації?';
|
||||||
|
$_['text_export_import'] = 'Експорт / Імпорт налаштувань';
|
||||||
|
$_['button_export_settings'] = 'Експорт налаштувань (JSON)';
|
||||||
|
$_['button_import_settings'] = 'Імпортувати налаштування';
|
||||||
|
$_['help_export_settings'] = 'Завантажити всі налаштування модуля в JSON-файл';
|
||||||
|
$_['help_import_settings'] = 'Завантажити раніше експортований JSON для відновлення налаштувань';
|
||||||
|
$_['text_import_success'] = 'Налаштування успішно імпортовані. Перезавантажте сторінку для застосування.';
|
||||||
|
$_['error_import_file'] = 'Файл не завантажено або помилка завантаження';
|
||||||
|
$_['error_import_invalid'] = 'Некоректний файл налаштувань (невірний модуль або формат)';
|
||||||
|
$_['text_click_to_load'] = 'Натисніть ↑ для завантаження даних диска';
|
||||||
|
$_['tab_formats'] = 'Формати зображень';
|
||||||
|
|
||||||
|
$_['text_disk_quota_legend'] = 'Ліміти диска хостингу (вручну)';
|
||||||
|
$_['text_disk_quota_desc'] = 'Якщо графік дискового простору показує некоректні дані (фізичний диск сервера замість ліміту вашого хостингу), ви можете вказати ліміти вашого тарифу вручну. Вкажіть 0, щоб використовувати автовизначення.';
|
||||||
|
$_['entry_disk_quota_total'] = 'Виділено диска на хостингу (ГБ)';
|
||||||
|
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
|
||||||
|
$_['entry_auto_disk'] = 'Автооновлення при вході';
|
||||||
|
|
||||||
|
// Cron Tasks
|
||||||
|
$_['entry_cron_task_name'] = 'Назва задачі';
|
||||||
|
$_['entry_cron_task_summary'] = 'Параметри стиснення';
|
||||||
|
$_['button_add_cron_task'] = 'Додати задачу';
|
||||||
|
$_['button_copy_cron_url'] = 'Копіювати URL';
|
||||||
|
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
|
||||||
|
|
||||||
|
// Licensing
|
||||||
|
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
|
||||||
|
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
|
||||||
|
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форуме або звернувшись у підтримку.';
|
||||||
|
$_['button_activate'] = 'Активувати модуль';
|
||||||
|
$_['text_support'] = 'Технічна підтримка';
|
||||||
|
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
|
||||||
|
|
||||||
|
// New settings
|
||||||
|
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
|
||||||
|
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
|
||||||
|
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
|
||||||
|
$_['help_wm_max_height'] = 'Якщо вихідне зображення водяного знака перевищує цю висоту, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
|
||||||
|
$_['entry_disk_status'] = 'Увімкнути сканування диска та графік';
|
||||||
|
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
|
||||||
|
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,637 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('DIR_APPLICATION')) { exit; }
|
||||||
|
|
||||||
|
class ControllerModuleImgOptiCron extends Controller {
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
@set_time_limit(0);
|
||||||
|
ini_set('memory_limit', '1024M');
|
||||||
|
|
||||||
|
$this->load->model('setting/setting');
|
||||||
|
$settings = $this->model_setting_setting->getSetting('module_img_opti');
|
||||||
|
|
||||||
|
if (PHP_SAPI !== 'cli') {
|
||||||
|
if (empty($this->request->get['key']) || $this->request->get['key'] !== $settings['module_img_opti_token']) {
|
||||||
|
die('Error: Unauthorized request. Invalid cron key.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($settings['module_img_opti_status'])) {
|
||||||
|
die('Error: Module Image Optimizer [NAT] is disabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($settings['module_img_opti_token']) || !$this->checkLicense($settings['module_img_opti_token'])) {
|
||||||
|
die('Error: Module token is invalid or license not verified.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = array();
|
||||||
|
if (!empty($settings['module_img_opti_cron_tasks'])) {
|
||||||
|
$tasks = json_decode(html_entity_decode($settings['module_img_opti_cron_tasks'], ENT_QUOTES, 'UTF-8'), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($tasks) || empty($tasks)) {
|
||||||
|
$legacy_entities = isset($settings['module_img_opti_cron_entities']) ? $settings['module_img_opti_cron_entities'] : array();
|
||||||
|
if (is_string($legacy_entities)) {
|
||||||
|
$legacy_entities = json_decode(html_entity_decode($legacy_entities, ENT_QUOTES, 'UTF-8'), true) ?: explode(',', $legacy_entities);
|
||||||
|
}
|
||||||
|
$legacy_folders = isset($settings['module_img_opti_cron_folders']) ? $settings['module_img_opti_cron_folders'] : array();
|
||||||
|
if (is_string($legacy_folders)) {
|
||||||
|
$legacy_folders = json_decode(html_entity_decode($legacy_folders, ENT_QUOTES, 'UTF-8'), true) ?: explode(',', $legacy_folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = array(
|
||||||
|
array(
|
||||||
|
'status' => 0,
|
||||||
|
'name' => 'Default Task',
|
||||||
|
'new_only' => isset($settings['module_img_opti_cron_new_only']) ? (int)$settings['module_img_opti_cron_new_only'] : 1,
|
||||||
|
'entities' => is_array($legacy_entities) ? $legacy_entities : array('product', 'category', 'manufacturer', 'banner', 'blog'),
|
||||||
|
'folder_mode' => isset($settings['module_img_opti_cron_folder_mode']) ? $settings['module_img_opti_cron_folder_mode'] : 'exclude',
|
||||||
|
'folders' => is_array($legacy_folders) ? $legacy_folders : array(),
|
||||||
|
'quality_override' => isset($settings['module_img_opti_cron_quality_override']) ? (int)$settings['module_img_opti_cron_quality_override'] : 0,
|
||||||
|
'jpg_quality' => isset($settings['module_img_opti_cron_jpg_quality']) ? (int)$settings['module_img_opti_cron_jpg_quality'] : 72,
|
||||||
|
'webp_quality' => isset($settings['module_img_opti_cron_webp_quality']) ? (int)$settings['module_img_opti_cron_webp_quality'] : 80,
|
||||||
|
'max_width' => isset($settings['module_img_opti_cron_max_width']) ? (int)$settings['module_img_opti_cron_max_width'] : 1600,
|
||||||
|
'max_height' => isset($settings['module_img_opti_cron_max_height']) ? (int)$settings['module_img_opti_cron_max_height'] : 1600
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks_to_run = array();
|
||||||
|
if (isset($this->request->get['task'])) {
|
||||||
|
$req_task = trim($this->request->get['task']);
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
if (strcasecmp($task['name'], $req_task) === 0) {
|
||||||
|
if (empty($task['status'])) {
|
||||||
|
die("Error: Requested cron task '" . htmlspecialchars($req_task) . "' is disabled.");
|
||||||
|
}
|
||||||
|
$tasks_to_run[] = $task;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($tasks_to_run)) {
|
||||||
|
die("Error: Requested cron task '" . htmlspecialchars($req_task) . "' was not found.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
if (!empty($task['status'])) {
|
||||||
|
$tasks_to_run[] = $task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($tasks_to_run)) {
|
||||||
|
die("Success: No enabled cron tasks found.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Image Optimizer [NAT] Cron Engine Started...\n";
|
||||||
|
echo "Found " . count($tasks_to_run) . " task(s) to execute.\n\n";
|
||||||
|
|
||||||
|
$engine = isset($settings['module_img_opti_engine']) && $settings['module_img_opti_engine'] == 'imagick' && extension_loaded('imagick') ? 'imagick' : 'gd';
|
||||||
|
$min_savings_bytes = isset($settings['module_img_opti_min_savings_bytes']) ? (int)$settings['module_img_opti_min_savings_bytes'] : 100;
|
||||||
|
$min_savings_percent = isset($settings['module_img_opti_min_savings_percent']) ? (float)$settings['module_img_opti_min_savings_percent'] : 0.3;
|
||||||
|
$threshold = isset($settings['module_img_opti_size_threshold']) ? (float)$settings['module_img_opti_size_threshold'] : 0;
|
||||||
|
|
||||||
|
$cacheFileOpt = DIR_CACHE . 'img_opti_optimized.json';
|
||||||
|
$cache = array();
|
||||||
|
if (file_exists($cacheFileOpt)) {
|
||||||
|
$content = @file_get_contents($cacheFileOpt);
|
||||||
|
if ($content) {
|
||||||
|
$decoded = json_decode($content, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$cache = $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$realImageDir = rtrim(str_replace('\\', '/', realpath(DIR_IMAGE)), '/');
|
||||||
|
$dirImageNorm = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/';
|
||||||
|
|
||||||
|
foreach ($tasks_to_run as $task) {
|
||||||
|
$taskName = $task['name'];
|
||||||
|
echo "--- Executing Task: $taskName ---\n";
|
||||||
|
|
||||||
|
$task_name_lower = strtolower($taskName);
|
||||||
|
$cacheFile = DIR_CACHE . 'img_opti_cron_last_run_' . md5($task_name_lower) . '.txt';
|
||||||
|
if ($task_name_lower === 'default task' && !file_exists($cacheFile) && file_exists(DIR_CACHE . 'img_opti_cron_last_run.txt')) {
|
||||||
|
@copy(DIR_CACHE . 'img_opti_cron_last_run.txt', $cacheFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$onlyNew = isset($task['new_only']) ? (int)$task['new_only'] : 1;
|
||||||
|
$lastRunTime = 0;
|
||||||
|
if ($onlyNew) {
|
||||||
|
$lastRunTime = file_exists($cacheFile) ? (int)file_get_contents($cacheFile) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$task_maxWidth = isset($settings['module_img_opti_max_width']) ? (int)$settings['module_img_opti_max_width'] : 1600;
|
||||||
|
$task_maxHeight = isset($settings['module_img_opti_max_height']) ? (int)$settings['module_img_opti_max_height'] : 1600;
|
||||||
|
$task_jpgQ = isset($settings['module_img_opti_jpg_quality']) ? (int)$settings['module_img_opti_jpg_quality'] : 72;
|
||||||
|
$task_pngQ = isset($settings['module_img_opti_png_quality']) ? (int)$settings['module_img_opti_png_quality'] : 7;
|
||||||
|
$task_webpQ = isset($settings['module_img_opti_webp_quality']) ? (int)$settings['module_img_opti_webp_quality'] : 80;
|
||||||
|
|
||||||
|
if (!empty($task['quality_override'])) {
|
||||||
|
$task_jpgQ = isset($task['jpg_quality']) ? (int)$task['jpg_quality'] : $task_jpgQ;
|
||||||
|
$task_webpQ = isset($task['webp_quality']) ? (int)$task['webp_quality'] : $task_webpQ;
|
||||||
|
$task_maxWidth = isset($task['max_width']) ? (int)$task['max_width'] : $task_maxWidth;
|
||||||
|
$task_maxHeight = isset($task['max_height']) ? (int)$task['max_height'] : $task_maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
$batchLimit = 100;
|
||||||
|
$dirsToScan = array();
|
||||||
|
$excludeDirs = array();
|
||||||
|
|
||||||
|
$folder_mode = isset($task['folder_mode']) ? $task['folder_mode'] : 'exclude';
|
||||||
|
$selected_folders = isset($task['folders']) ? $task['folders'] : array();
|
||||||
|
if (is_string($selected_folders)) {
|
||||||
|
$selected_folders = json_decode(html_entity_decode($selected_folders, ENT_QUOTES, 'UTF-8'), true) ?: explode(',', $selected_folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($folder_mode === 'exclude') {
|
||||||
|
$dirsToScan[] = $realImageDir . '/catalog';
|
||||||
|
foreach ($selected_folders as $folder) {
|
||||||
|
$folder = trim(str_replace(array('../', '..\\', "\0"), '', $folder));
|
||||||
|
if ($folder === '') continue;
|
||||||
|
$path = realpath(DIR_IMAGE . $folder);
|
||||||
|
if ($path && is_dir($path) && strpos(str_replace('\\', '/', $path), $realImageDir) === 0) {
|
||||||
|
$excludeDirs[] = str_replace('\\', '/', $path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach ($selected_folders as $folder) {
|
||||||
|
$folder = trim(str_replace(array('../', '..\\', "\0"), '', $folder));
|
||||||
|
if ($folder === '') continue;
|
||||||
|
$path = realpath(DIR_IMAGE . $folder);
|
||||||
|
if ($path && is_dir($path) && strpos(str_replace('\\', '/', $path), $realImageDir) === 0) {
|
||||||
|
$dirsToScan[] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($dirsToScan)) {
|
||||||
|
$dirsToScan[] = $realImageDir . '/catalog';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cron_entities = isset($task['entities']) ? $task['entities'] : array();
|
||||||
|
if (is_string($cron_entities)) {
|
||||||
|
$cron_entities = json_decode(html_entity_decode($cron_entities, ENT_QUOTES, 'UTF-8'), true) ?: explode(',', $cron_entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Mode: " . ($onlyNew ? "Scanning for NEW files since " . date('Y-m-d H:i:s', $lastRunTime) : "Scanning ALL files") . "\n";
|
||||||
|
echo "Quality Settings - JPG: $task_jpgQ%, WebP: $task_webpQ%, Max size: " . $task_maxWidth . "x" . $task_maxHeight . "px\n";
|
||||||
|
|
||||||
|
$filesToProcess = array();
|
||||||
|
$currentTime = time();
|
||||||
|
|
||||||
|
foreach ($dirsToScan as $dir) {
|
||||||
|
if (!is_dir($dir)) continue;
|
||||||
|
try {
|
||||||
|
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS));
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
if ($file->isFile()) {
|
||||||
|
$realPath = realpath($file->getPathname());
|
||||||
|
if (!$realPath || strpos(str_replace('\\', '/', $realPath), $realImageDir) !== 0) continue;
|
||||||
|
|
||||||
|
$normalizedPath = str_replace('\\', '/', $realPath);
|
||||||
|
$in_excluded = false;
|
||||||
|
foreach ($excludeDirs as $ex_dir) {
|
||||||
|
if (strpos($normalizedPath, $ex_dir) === 0) {
|
||||||
|
$in_excluded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($in_excluded) continue;
|
||||||
|
|
||||||
|
$ext = strtolower($file->getExtension());
|
||||||
|
if (in_array($ext, array('jpg', 'jpeg', 'png', 'webp', 'gif'))) {
|
||||||
|
$filePathNorm = str_replace('\\', '/', $realPath);
|
||||||
|
$relPath = $this->getRelativeImagePath($filePathNorm);
|
||||||
|
$skip = false;
|
||||||
|
if ($onlyNew && isset($cache[$relPath])) {
|
||||||
|
clearstatcache(true, $filePathNorm);
|
||||||
|
$size = @filesize($filePathNorm);
|
||||||
|
$mtime = @filemtime($filePathNorm);
|
||||||
|
if ($size === $cache[$relPath]['size'] && $mtime === $cache[$relPath]['mtime']) {
|
||||||
|
$skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($skip) continue;
|
||||||
|
|
||||||
|
if ($file->getMTime() > $lastRunTime) {
|
||||||
|
$relativePath = $this->getRelativeImagePath($normalizedPath);
|
||||||
|
$entities = $this->getEntitiesByImagePath($relativePath);
|
||||||
|
$has_match = false;
|
||||||
|
|
||||||
|
if (empty($entities)) {
|
||||||
|
if (empty($cron_entities)) {
|
||||||
|
$has_match = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (empty($cron_entities)) {
|
||||||
|
$has_match = true;
|
||||||
|
} else {
|
||||||
|
foreach ($entities as $entity_html) {
|
||||||
|
if (strpos($entity_html, 'Product') !== false && in_array('product', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Category') !== false && in_array('category', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Manufacturer') !== false && in_array('manufacturer', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Banner') !== false && in_array('banner', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Blog') !== false && in_array('blog', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Simple Blog') !== false && in_array('blog', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'OCT Blog') !== false && in_array('blog', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'NewsBlog') !== false && in_array('blog', $cron_entities)) $has_match = true;
|
||||||
|
if (strpos($entity_html, 'Information') !== false && in_array('blog', $cron_entities)) $has_match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($has_match) {
|
||||||
|
$filesToProcess[] = $realPath;
|
||||||
|
if (count($filesToProcess) >= $batchLimit) {
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($filesToProcess)) {
|
||||||
|
file_put_contents($cacheFile, $currentTime);
|
||||||
|
echo "No new files found for optimization in this task.\n\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$processedCount = 0;
|
||||||
|
$savedBytes = 0;
|
||||||
|
$cache_updates = array();
|
||||||
|
|
||||||
|
foreach ($filesToProcess as $filePath) {
|
||||||
|
$realFilePath = realpath($filePath);
|
||||||
|
if (!$realFilePath || strpos(str_replace('\\', '/', $realFilePath), $dirImageNorm) !== 0 || !is_file($realFilePath)) continue;
|
||||||
|
|
||||||
|
$relativePath = $this->getRelativeImagePath($realFilePath);
|
||||||
|
$fileParams = $this->getImageParameters($relativePath, array(
|
||||||
|
'module_img_opti_folder_rules' => isset($settings['module_img_opti_folder_rules']) ? $settings['module_img_opti_folder_rules'] : array(),
|
||||||
|
'module_img_opti_jpg_quality' => $task_jpgQ,
|
||||||
|
'module_img_opti_png_quality' => $task_pngQ,
|
||||||
|
'module_img_opti_webp_quality' => $task_webpQ,
|
||||||
|
'module_img_opti_max_width' => $task_maxWidth,
|
||||||
|
'module_img_opti_max_height' => $task_maxHeight
|
||||||
|
));
|
||||||
|
|
||||||
|
$oldSize = filesize($realFilePath);
|
||||||
|
$tempPath = DIR_CACHE . 'opti_cron_temp_' . md5($realFilePath);
|
||||||
|
|
||||||
|
if ($this->optimizeImage($realFilePath, $tempPath, $fileParams['max_width'], $fileParams['max_height'], $fileParams['jpg_quality'], $fileParams['png_quality'], $fileParams['webp_quality'], $engine)) {
|
||||||
|
clearstatcache();
|
||||||
|
$newSize = filesize($tempPath);
|
||||||
|
$increasePercent = ($oldSize > 0) ? (($newSize - $oldSize) / $oldSize) * 100 : 0;
|
||||||
|
|
||||||
|
$savedBytesFile = $oldSize - $newSize;
|
||||||
|
$savedPercentFile = ($oldSize > 0) ? ($savedBytesFile / $oldSize) * 100 : 0;
|
||||||
|
$is_too_small_gain = ($savedBytesFile < $min_savings_bytes || $savedPercentFile < $min_savings_percent);
|
||||||
|
|
||||||
|
if ($increasePercent > $threshold || $is_too_small_gain) {
|
||||||
|
@unlink($tempPath);
|
||||||
|
$cacheKey = $this->getRelativeImagePath($realFilePath);
|
||||||
|
$cache_updates[$cacheKey] = array(
|
||||||
|
'size' => $oldSize,
|
||||||
|
'mtime' => @filemtime($realFilePath)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
@rename($tempPath, $realFilePath);
|
||||||
|
$savedBytes += ($oldSize - $newSize);
|
||||||
|
$processedCount++;
|
||||||
|
clearstatcache(true, $realFilePath);
|
||||||
|
$cacheKey = $this->getRelativeImagePath($realFilePath);
|
||||||
|
$cache_updates[$cacheKey] = array(
|
||||||
|
'size' => @filesize($realFilePath),
|
||||||
|
'mtime' => @filemtime($realFilePath)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@unlink($tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
gc_collect_cycles();
|
||||||
|
|
||||||
|
$memLimit = $this->getMemoryLimitInBytesHelper();
|
||||||
|
if (memory_get_usage(true) > 0.80 * $memLimit) {
|
||||||
|
echo "Memory limit reached (80%). Stopping task execution batch...\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cache_updates)) {
|
||||||
|
if (file_exists($cacheFileOpt)) {
|
||||||
|
$content = @file_get_contents($cacheFileOpt);
|
||||||
|
if ($content) {
|
||||||
|
$decoded = json_decode($content, true);
|
||||||
|
if (is_array($decoded)) {
|
||||||
|
$cache = array_merge($cache, $decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($cache_updates as $k => $v) {
|
||||||
|
$cache[$k] = $v;
|
||||||
|
}
|
||||||
|
@file_put_contents($cacheFileOpt, json_encode($cache, JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($processedCount > 0 || count($filesToProcess) < $batchLimit) {
|
||||||
|
file_put_contents($cacheFile, $currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Task Done: Processed $processedCount files. Saved space: " . $this->formatBytes($savedBytes) . ".\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "All cron tasks execution finished.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEntitiesByImagePath($image_path) {
|
||||||
|
$entities = array();
|
||||||
|
$escaped = $this->db->escape((string)$image_path);
|
||||||
|
$catalog_url = (defined('HTTPS_CATALOG') && HTTPS_CATALOG) ? HTTPS_CATALOG : (defined('HTTP_CATALOG') ? HTTP_CATALOG : '');
|
||||||
|
$query = $this->db->query("SELECT product_id, (SELECT name FROM `" . DB_PREFIX . "product_description` pd WHERE pd.product_id = p.product_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "' LIMIT 1) AS name FROM `" . DB_PREFIX . "product` p WHERE image = '" . $escaped . "'");
|
||||||
|
foreach ($query->rows as $row) {
|
||||||
|
$entities[] = 'Product ID ' . $row['product_id'];
|
||||||
|
}
|
||||||
|
$query = $this->db->query("SELECT pi.product_id FROM `" . DB_PREFIX . "product_image` pi WHERE image = '" . $escaped . "'");
|
||||||
|
foreach ($query->rows as $row) {
|
||||||
|
$entities[] = 'Product ID ' . $row['product_id'];
|
||||||
|
}
|
||||||
|
$query = $this->db->query("SELECT category_id FROM `" . DB_PREFIX . "category` c WHERE image = '" . $escaped . "'");
|
||||||
|
foreach ($query->rows as $row) {
|
||||||
|
$entities[] = 'Category ID ' . $row['category_id'];
|
||||||
|
}
|
||||||
|
$query = $this->db->query("SELECT manufacturer_id, name FROM `" . DB_PREFIX . "manufacturer` WHERE image = '" . $escaped . "'");
|
||||||
|
foreach ($query->rows as $row) {
|
||||||
|
$entities[] = 'Manufacturer ID ' . $row['manufacturer_id'];
|
||||||
|
}
|
||||||
|
$table_query = $this->db->query("SHOW TABLES LIKE '" . DB_PREFIX . "banner_image'");
|
||||||
|
if ($table_query->num_rows) {
|
||||||
|
$query_banner = $this->db->query("SELECT bi.banner_id FROM `" . DB_PREFIX . "banner_image` bi WHERE bi.image = '" . $escaped . "'");
|
||||||
|
foreach ($query_banner->rows as $row) {
|
||||||
|
$entities[] = 'Banner ID ' . $row['banner_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$blog_tables = array(
|
||||||
|
'oct_blog_article' => array('id' => 'blog_article_id'),
|
||||||
|
'simple_blog_article' => array('id' => 'simple_blog_article_id'),
|
||||||
|
'newsblog_article' => array('id' => 'article_id'),
|
||||||
|
'information' => array('id' => 'information_id')
|
||||||
|
);
|
||||||
|
foreach ($blog_tables as $table => $cfg) {
|
||||||
|
$table_query = $this->db->query("SHOW TABLES LIKE '" . DB_PREFIX . $table . "'");
|
||||||
|
if ($table_query->num_rows) {
|
||||||
|
$column_query = $this->db->query("SHOW COLUMNS FROM `" . DB_PREFIX . $table . "` LIKE 'image'");
|
||||||
|
if ($column_query->num_rows) {
|
||||||
|
try {
|
||||||
|
$query_blog = $this->db->query("SELECT `" . $cfg['id'] . "` FROM `" . DB_PREFIX . $table . "` WHERE image = '" . $escaped . "' LIMIT 5");
|
||||||
|
foreach ($query_blog->rows as $row) {
|
||||||
|
$entities[] = 'Blog ID ' . $row[$cfg['id']];
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function optimizeImage($src, $dest, $maxWidth, $maxHeight, $jpgQuality, $pngCompression, $webpQuality, $engine = 'gd') {
|
||||||
|
if ($engine == 'imagick' && extension_loaded('imagick')) {
|
||||||
|
try {
|
||||||
|
$img = new Imagick($src);
|
||||||
|
$img->transformImageColorspace(Imagick::COLORSPACE_SRGB);
|
||||||
|
|
||||||
|
$width = $img->getImageWidth();
|
||||||
|
$height = $img->getImageHeight();
|
||||||
|
$ext = strtolower($this->mb_pathinfo($src, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
if (($maxWidth > 0 && $width > $maxWidth) || ($maxHeight > 0 && $height > $maxHeight)) {
|
||||||
|
$targetW = $maxWidth;
|
||||||
|
$targetH = $maxHeight;
|
||||||
|
if ($targetW == 0) {
|
||||||
|
$targetW = (int)round($width * ($targetH / $height));
|
||||||
|
}
|
||||||
|
if ($targetH == 0) {
|
||||||
|
$targetH = (int)round($height * ($targetW / $width));
|
||||||
|
}
|
||||||
|
$img->resizeImage($targetW, $targetH, Imagick::FILTER_LANCZOS, 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$img->stripImage();
|
||||||
|
|
||||||
|
if ($ext === 'png') {
|
||||||
|
$img->setImageFormat('png');
|
||||||
|
$img->setCompressionQuality($pngCompression * 10);
|
||||||
|
} elseif ($ext === 'webp') {
|
||||||
|
$img->setImageFormat('webp');
|
||||||
|
$img->setCompressionQuality($webpQuality);
|
||||||
|
} else {
|
||||||
|
$img->setImageFormat('jpeg');
|
||||||
|
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
|
||||||
|
$img->setCompressionQuality($jpgQuality);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $img->writeImage($dest);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$engine = 'gd';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($engine == 'gd') {
|
||||||
|
$img = @imagecreatefromstring(file_get_contents($src));
|
||||||
|
if (!$img) return false;
|
||||||
|
|
||||||
|
$width = imagesx($img);
|
||||||
|
$height = imagesy($img);
|
||||||
|
$ext = strtolower($this->mb_pathinfo($src, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
if ($ext === 'png' || $ext === 'webp') {
|
||||||
|
imagealphablending($img, false);
|
||||||
|
imagesavealpha($img, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($maxWidth > 0 && $width > $maxWidth) || ($maxHeight > 0 && $height > $maxHeight)) {
|
||||||
|
$ratioX = ($maxWidth > 0) ? ($maxWidth / $width) : 1;
|
||||||
|
$ratioY = ($maxHeight > 0) ? ($maxHeight / $height) : 1;
|
||||||
|
$ratio = min($ratioX, $ratioY);
|
||||||
|
|
||||||
|
$newW = (int)($width * $ratio);
|
||||||
|
$newH = (int)($height * $ratio);
|
||||||
|
|
||||||
|
$newImg = imagecreatetruecolor($newW, $newH);
|
||||||
|
|
||||||
|
if ($ext === 'png' || $ext === 'webp') {
|
||||||
|
imagealphablending($newImg, false);
|
||||||
|
imagesavealpha($newImg, true);
|
||||||
|
$transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
|
||||||
|
imagefill($newImg, 0, 0, $transparent);
|
||||||
|
} else {
|
||||||
|
$white = imagecolorallocate($newImg, 255, 255, 255);
|
||||||
|
imagefill($newImg, 0, 0, $white);
|
||||||
|
}
|
||||||
|
|
||||||
|
imagecopyresampled($newImg, $img, 0, 0, 0, 0, $newW, $newH, $width, $height);
|
||||||
|
imagedestroy($img);
|
||||||
|
$img = $newImg;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = false;
|
||||||
|
if ($ext === 'png') {
|
||||||
|
$res = imagepng($img, $dest, $pngCompression);
|
||||||
|
} elseif ($ext === 'webp' && function_exists('imagewebp')) {
|
||||||
|
$res = imagewebp($img, $dest, $webpQuality);
|
||||||
|
} else {
|
||||||
|
$res = imagejpeg($img, $dest, $jpgQuality);
|
||||||
|
}
|
||||||
|
|
||||||
|
imagedestroy($img);
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkLicense($key) {
|
||||||
|
$host = explode(':', str_replace(array('http://', 'https://', 'www.'), '', strtolower($this->request->server['HTTP_HOST'])))[0];
|
||||||
|
if ($host === 'localhost' || $host === '127.0.0.1') return true;
|
||||||
|
if (empty($key)) return false;
|
||||||
|
$parts = explode('.', $host);
|
||||||
|
$v_k = 'k'; $v_to = 'to'; $v_bit = '_lom'; $v_end = 'aet_'; $v_img = 'image_'; $v_opt = 'optimizer_'; $v_mid = 'tot'; $v_core = '_redis'; $v_sfx = 'ka';
|
||||||
|
$salt = ($v_k . $v_to . $v_bit) . ($v_end . $v_img . $v_opt . $v_mid) . ($v_core . $v_sfx);
|
||||||
|
while(count($parts) >= 2) {
|
||||||
|
$domain = implode('.', $parts);
|
||||||
|
if ($key === strtoupper(implode('-', str_split(substr(md5($domain . $salt), 0, 16), 4)))) return true;
|
||||||
|
array_shift($parts);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatBytes($bytes) {
|
||||||
|
if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
|
||||||
|
if ($bytes >= 1024) return round($bytes / 1024, 0) . ' KB';
|
||||||
|
return $bytes . ' B';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getImageParameters($relativePath, $globalSettings) {
|
||||||
|
$rules = array();
|
||||||
|
if (isset($globalSettings['module_img_opti_folder_rules'])) {
|
||||||
|
if (is_array($globalSettings['module_img_opti_folder_rules'])) {
|
||||||
|
$rules = $globalSettings['module_img_opti_folder_rules'];
|
||||||
|
} else {
|
||||||
|
$rules = json_decode(html_entity_decode((string)$globalSettings['module_img_opti_folder_rules'], ENT_QUOTES, 'UTF-8'), true);
|
||||||
|
if (!is_array($rules)) {
|
||||||
|
$rules = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$jpgQ = isset($globalSettings['module_img_opti_jpg_quality']) ? (int)$globalSettings['module_img_opti_jpg_quality'] : 72;
|
||||||
|
$pngQ = isset($globalSettings['module_img_opti_png_quality']) ? (int)$globalSettings['module_img_opti_png_quality'] : 7;
|
||||||
|
$webpQ = isset($globalSettings['module_img_opti_webp_quality']) ? (int)$globalSettings['module_img_opti_webp_quality'] : 80;
|
||||||
|
$maxW = isset($globalSettings['module_img_opti_max_width']) ? (int)$globalSettings['module_img_opti_max_width'] : 1600;
|
||||||
|
$maxH = isset($globalSettings['module_img_opti_max_height']) ? (int)$globalSettings['module_img_opti_max_height'] : 1600;
|
||||||
|
|
||||||
|
$relativePath = str_replace('\\', '/', $relativePath);
|
||||||
|
$relativePath = ltrim($relativePath, '/');
|
||||||
|
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
if (empty($rule['folder'])) continue;
|
||||||
|
$folder = str_replace('\\', '/', $rule['folder']);
|
||||||
|
$folder = trim($folder, '/');
|
||||||
|
|
||||||
|
if ($folder !== '' && strpos($relativePath, $folder) !== false) {
|
||||||
|
if (isset($rule['jpg_quality']) && $rule['jpg_quality'] !== '') {
|
||||||
|
$jpgQ = (int)$rule['jpg_quality'];
|
||||||
|
}
|
||||||
|
if (isset($rule['webp_quality']) && $rule['webp_quality'] !== '') {
|
||||||
|
$webpQ = (int)$rule['webp_quality'];
|
||||||
|
}
|
||||||
|
if (isset($rule['max_width']) && $rule['max_width'] !== '') {
|
||||||
|
$maxW = (int)$rule['max_width'];
|
||||||
|
}
|
||||||
|
if (isset($rule['max_height']) && $rule['max_height'] !== '') {
|
||||||
|
$maxH = (int)$rule['max_height'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'jpg_quality' => $jpgQ,
|
||||||
|
'png_quality' => $pngQ,
|
||||||
|
'webp_quality' => $webpQ,
|
||||||
|
'max_width' => $maxW,
|
||||||
|
'max_height' => $maxH
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMemoryLimitInBytesHelper() {
|
||||||
|
$limit = ini_get('memory_limit');
|
||||||
|
if (!$limit || (int)$limit === -1) return 512 * 1024 * 1024;
|
||||||
|
$unit = strtolower(substr($limit, -1));
|
||||||
|
$bytes = (int)$limit;
|
||||||
|
switch ($unit) {
|
||||||
|
case 'g': $bytes *= 1024;
|
||||||
|
case 'm': $bytes *= 1024;
|
||||||
|
case 'k': $bytes *= 1024;
|
||||||
|
}
|
||||||
|
return $bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getRelativeImagePath($absolutePath) {
|
||||||
|
$absolutePath = str_replace('\\', '/', $absolutePath);
|
||||||
|
$dirImageNorm = str_replace('\\', '/', DIR_IMAGE);
|
||||||
|
$rel = $absolutePath;
|
||||||
|
if (strpos(strtolower($absolutePath), strtolower($dirImageNorm)) === 0) {
|
||||||
|
$rel = substr($absolutePath, strlen($dirImageNorm));
|
||||||
|
}
|
||||||
|
$rel = ltrim($rel, '/');
|
||||||
|
if (mb_check_encoding($rel, 'UTF-8')) {
|
||||||
|
return $rel;
|
||||||
|
}
|
||||||
|
$converted = @mb_convert_encoding($rel, 'UTF-8', 'Windows-1251');
|
||||||
|
return $converted ? $converted : $rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mb_pathinfo($path, $options = null) {
|
||||||
|
$ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
|
||||||
|
$path = str_replace('\\', '/', $path);
|
||||||
|
$last_slash = strrpos($path, '/');
|
||||||
|
if ($last_slash !== false) {
|
||||||
|
$ret['dirname'] = substr($path, 0, $last_slash);
|
||||||
|
$basename = substr($path, $last_slash + 1);
|
||||||
|
} else {
|
||||||
|
$basename = $path;
|
||||||
|
}
|
||||||
|
$ret['basename'] = $basename;
|
||||||
|
$last_dot = strrpos($basename, '.');
|
||||||
|
if ($last_dot !== false) {
|
||||||
|
$ret['filename'] = substr($basename, 0, $last_dot);
|
||||||
|
$ret['extension'] = substr($basename, $last_dot + 1);
|
||||||
|
} else {
|
||||||
|
$ret['filename'] = $basename;
|
||||||
|
}
|
||||||
|
if ($options === PATHINFO_DIRNAME) return $ret['dirname'];
|
||||||
|
if ($options === PATHINFO_BASENAME) return $ret['basename'];
|
||||||
|
if ($options === PATHINFO_EXTENSION) return $ret['extension'];
|
||||||
|
if ($options === PATHINFO_FILENAME) return $ret['filename'];
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function safe_utf8($str) {
|
||||||
|
$str = (string)$str;
|
||||||
|
if (mb_check_encoding($str, 'UTF-8')) {
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
$converted = @mb_convert_encoding($str, 'UTF-8', 'Windows-1251');
|
||||||
|
return $converted ? $converted : $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mb_basename($path) {
|
||||||
|
$path = str_replace('\\', '/', $path);
|
||||||
|
$parts = explode('/', $path);
|
||||||
|
return end($parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,672 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('DIR_APPLICATION')) { exit; }
|
||||||
|
|
||||||
|
class ModelModuleImgOpti extends Model {
|
||||||
|
|
||||||
|
public function applyDynamicWatermark($cached_file, $original_file, $is_preview = false) {
|
||||||
|
$realCachedFile = realpath($cached_file);
|
||||||
|
$realImageDir = realpath(DIR_IMAGE);
|
||||||
|
if (!$realCachedFile || stripos(str_replace('\\', '/', $realCachedFile), str_replace('\\', '/', $realImageDir)) !== 0) return false;
|
||||||
|
|
||||||
|
$original_file_normalized = str_replace('\\', '/', (string)$original_file);
|
||||||
|
$escaped_original = $this->db->escape($original_file_normalized);
|
||||||
|
$matched_rules = array();
|
||||||
|
|
||||||
|
if ($is_preview) {
|
||||||
|
$matched_rules[] = array(
|
||||||
|
'status' => 1,
|
||||||
|
'type' => $this->config->get('module_img_opti_wm_type'),
|
||||||
|
'image' => $this->config->get('module_img_opti_wm_image'),
|
||||||
|
'text_val' => $this->config->get('module_img_opti_wm_text_val'),
|
||||||
|
'position' => $this->config->get('module_img_opti_wm_position'),
|
||||||
|
'opacity' => $this->config->get('module_img_opti_wm_opacity'),
|
||||||
|
'angle' => $this->config->get('module_img_opti_wm_angle'),
|
||||||
|
'corner_radius' => $this->config->get('module_img_opti_wm_corner_radius'),
|
||||||
|
'size_type' => $this->config->get('module_img_opti_wm_size_type'),
|
||||||
|
'size_percent' => $this->config->get('module_img_opti_wm_size_percent'),
|
||||||
|
'text_color' => $this->config->get('module_img_opti_wm_text_color'),
|
||||||
|
'text_font' => $this->config->get('module_img_opti_wm_text_font')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$rules_json = $this->config->get('module_img_opti_wm_rules');
|
||||||
|
$rules = json_decode(html_entity_decode($rules_json, ENT_QUOTES, 'UTF-8'), true) ?: array();
|
||||||
|
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
if (empty($rule['status'])) continue;
|
||||||
|
|
||||||
|
$targets = array();
|
||||||
|
if (!empty($rule['target_product'])) $targets[] = 'product';
|
||||||
|
if (!empty($rule['target_category'])) $targets[] = 'category';
|
||||||
|
if (!empty($rule['target_brand'])) $targets[] = 'manufacturer';
|
||||||
|
if (!empty($rule['target_banner'])) $targets[] = 'banner';
|
||||||
|
if (!empty($rule['target_blog'])) $targets[] = 'blog';
|
||||||
|
|
||||||
|
$is_match = false;
|
||||||
|
$product_id = 0;
|
||||||
|
$manufacturer_id = 0;
|
||||||
|
|
||||||
|
if (in_array('product', $targets)) {
|
||||||
|
$query_prod = $this->db->query("SELECT product_id, manufacturer_id FROM `" . DB_PREFIX . "product` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_prod->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
$product_id = (int)$query_prod->row['product_id'];
|
||||||
|
$manufacturer_id = (int)$query_prod->row['manufacturer_id'];
|
||||||
|
} else {
|
||||||
|
$query_prod_add = $this->db->query("SELECT product_id FROM `" . DB_PREFIX . "product_image` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_prod_add->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
$product_id = (int)$query_prod_add->row['product_id'];
|
||||||
|
$query_p_info = $this->db->query("SELECT manufacturer_id FROM `" . DB_PREFIX . "product` WHERE product_id = '" . $product_id . "' LIMIT 1");
|
||||||
|
$manufacturer_id = $query_p_info->num_rows ? (int)$query_p_info->row['manufacturer_id'] : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_match) {
|
||||||
|
$categories = array();
|
||||||
|
if (!empty($rule['categories'])) {
|
||||||
|
foreach ($rule['categories'] as $c) {
|
||||||
|
$categories[] = is_array($c) ? (int)$c['id'] : (int)$c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$brands = array();
|
||||||
|
if (!empty($rule['brands'])) {
|
||||||
|
foreach ($rule['brands'] as $b) {
|
||||||
|
$brands[] = is_array($b) ? (int)$b['id'] : (int)$b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$prod_categories = array();
|
||||||
|
$query_p_cats = $this->db->query("SELECT category_id FROM `" . DB_PREFIX . "product_to_category` WHERE product_id = '" . $product_id . "'");
|
||||||
|
foreach ($query_p_cats->rows as $row) {
|
||||||
|
$prod_categories[] = (int)$row['category_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$in_selected_cats = false;
|
||||||
|
if (!empty($categories)) {
|
||||||
|
foreach ($prod_categories as $c_id) {
|
||||||
|
if (in_array($c_id, $categories)) {
|
||||||
|
$in_selected_cats = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$in_selected_brands = false;
|
||||||
|
if (!empty($brands) && in_array($manufacturer_id, $brands)) {
|
||||||
|
$in_selected_brands = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter_mode = isset($rule['filter_mode']) ? $rule['filter_mode'] : 'include';
|
||||||
|
if ($filter_mode === 'include') {
|
||||||
|
if (!empty($categories) || !empty($brands)) {
|
||||||
|
if (!$in_selected_cats && !$in_selected_brands) {
|
||||||
|
$is_match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($in_selected_cats || $in_selected_brands) {
|
||||||
|
$is_match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_match && in_array('category', $targets)) {
|
||||||
|
$query_cat = $this->db->query("SELECT category_id FROM `" . DB_PREFIX . "category` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_cat->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
$category_id = (int)$query_cat->row['category_id'];
|
||||||
|
$categories = array();
|
||||||
|
if (!empty($rule['categories'])) {
|
||||||
|
foreach ($rule['categories'] as $c) {
|
||||||
|
$categories[] = is_array($c) ? (int)$c['id'] : (int)$c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($categories)) {
|
||||||
|
$in_selected = in_array($category_id, $categories);
|
||||||
|
$filter_mode = isset($rule['filter_mode']) ? $rule['filter_mode'] : 'include';
|
||||||
|
if ($filter_mode === 'include' && !$in_selected) {
|
||||||
|
$is_match = false;
|
||||||
|
} elseif ($filter_mode === 'exclude' && $in_selected) {
|
||||||
|
$is_match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_match && in_array('manufacturer', $targets)) {
|
||||||
|
$query_brand = $this->db->query("SELECT manufacturer_id FROM `" . DB_PREFIX . "manufacturer` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_brand->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
$manufacturer_id = (int)$query_brand->row['manufacturer_id'];
|
||||||
|
$brands = array();
|
||||||
|
if (!empty($rule['brands'])) {
|
||||||
|
foreach ($rule['brands'] as $b) {
|
||||||
|
$brands[] = is_array($b) ? (int)$b['id'] : (int)$b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($brands)) {
|
||||||
|
$in_selected = in_array($manufacturer_id, $brands);
|
||||||
|
$filter_mode = isset($rule['filter_mode']) ? $rule['filter_mode'] : 'include';
|
||||||
|
if ($filter_mode === 'include' && !$in_selected) {
|
||||||
|
$is_match = false;
|
||||||
|
} elseif ($filter_mode === 'exclude' && $in_selected) {
|
||||||
|
$is_match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_match && in_array('banner', $targets)) {
|
||||||
|
$table_query = $this->db->query("SHOW TABLES LIKE '" . DB_PREFIX . "banner_image'");
|
||||||
|
if ($table_query->num_rows) {
|
||||||
|
$query_banner = $this->db->query("SELECT banner_image_id FROM `" . DB_PREFIX . "banner_image` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_banner->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_match && in_array('blog', $targets)) {
|
||||||
|
$blog_tables = array(
|
||||||
|
'oct_blog_article',
|
||||||
|
'simple_blog_article',
|
||||||
|
'newsblog_article',
|
||||||
|
'information'
|
||||||
|
);
|
||||||
|
foreach ($blog_tables as $b_table) {
|
||||||
|
$table_query = $this->db->query("SHOW TABLES LIKE '" . DB_PREFIX . $b_table . "'");
|
||||||
|
if ($table_query->num_rows) {
|
||||||
|
$column_query = $this->db->query("SHOW COLUMNS FROM `" . DB_PREFIX . $b_table . "` LIKE 'image'");
|
||||||
|
if ($column_query->num_rows) {
|
||||||
|
$query_blog = $this->db->query("SELECT * FROM `" . DB_PREFIX . $b_table . "` WHERE image = '" . $escaped_original . "' LIMIT 1");
|
||||||
|
if ($query_blog->num_rows) {
|
||||||
|
$is_match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($is_match) {
|
||||||
|
$matched_rules[] = $rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($matched_rules)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$engine = preg_replace('/[^a-z]/', '', strtolower($this->config->get('module_img_opti_engine')));
|
||||||
|
$hasImagick = extension_loaded('imagick');
|
||||||
|
|
||||||
|
if ($engine === 'imagick' && $hasImagick) {
|
||||||
|
try {
|
||||||
|
$img = new Imagick($realCachedFile);
|
||||||
|
foreach ($matched_rules as $rule) {
|
||||||
|
$type = preg_replace('/[^a-z]/', '', $rule['type']);
|
||||||
|
$imagePathStr = str_replace(array('../', '..\\', ' '), '', (string)$rule['image']);
|
||||||
|
$imagePath = realpath(DIR_IMAGE . $imagePathStr);
|
||||||
|
if ($imagePath && stripos(str_replace('\\', '/', $imagePath), str_replace('\\', '/', $realImageDir)) !== 0) {
|
||||||
|
$imagePath = false;
|
||||||
|
}
|
||||||
|
$textVal = (string)$rule['text_val'];
|
||||||
|
$position = (int)$rule['position'];
|
||||||
|
$opacity = (int)$rule['opacity'];
|
||||||
|
$size_type = isset($rule['size_type']) ? $rule['size_type'] : 'original';
|
||||||
|
$size_percent = isset($rule['size_percent']) ? (int)$rule['size_percent'] : 20;
|
||||||
|
$angle = isset($rule['angle']) ? (int)$rule['angle'] : 0;
|
||||||
|
$corner_radius = isset($rule['corner_radius']) ? (int)$rule['corner_radius'] : 0;
|
||||||
|
$text_font = isset($rule['text_font']) ? (string)$rule['text_font'] : '';
|
||||||
|
$text_color = isset($rule['text_color']) ? (string)$rule['text_color'] : '#ffffff';
|
||||||
|
|
||||||
|
if ($type === 'image') {
|
||||||
|
if (!$imagePath || !is_file($imagePath)) continue;
|
||||||
|
}
|
||||||
|
if ($type === 'text' && empty($textVal)) continue;
|
||||||
|
|
||||||
|
$imgW = $img->getImageWidth();
|
||||||
|
$imgH = $img->getImageHeight();
|
||||||
|
|
||||||
|
if ($type === 'image') {
|
||||||
|
$wm = new Imagick($imagePath);
|
||||||
|
|
||||||
|
$max_wm_w = (int)$this->config->get('module_img_opti_wm_max_width');
|
||||||
|
if ($max_wm_w <= 0) $max_wm_w = 800;
|
||||||
|
$max_wm_h = (int)$this->config->get('module_img_opti_wm_max_height');
|
||||||
|
if ($max_wm_h <= 0) $max_wm_h = 800;
|
||||||
|
|
||||||
|
$wmW = $wm->getImageWidth();
|
||||||
|
$wmH = $wm->getImageHeight();
|
||||||
|
if ($wmW > $max_wm_w || $wmH > $max_wm_h) {
|
||||||
|
$ratio = min($max_wm_w / $wmW, $max_wm_h / $wmH);
|
||||||
|
$new_wm_w = intval($wmW * $ratio);
|
||||||
|
$new_wm_h = intval($wmH * $ratio);
|
||||||
|
if ($new_wm_w > 0 && $new_wm_h > 0) {
|
||||||
|
$wm->resizeImage($new_wm_w, $new_wm_h, Imagick::FILTER_LANCZOS, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($size_type === 'percent' && $size_percent > 0) {
|
||||||
|
$targetW = intval(($imgW * $size_percent) / 100);
|
||||||
|
$targetH = intval(($wm->getImageHeight() / $wm->getImageWidth()) * $targetW);
|
||||||
|
if ($targetW > 0 && $targetH > 0) {
|
||||||
|
$wm->resizeImage($targetW, $targetH, Imagick::FILTER_LANCZOS, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($corner_radius > 0) {
|
||||||
|
$wm->roundCorners($corner_radius, $corner_radius);
|
||||||
|
}
|
||||||
|
if ($angle != 0) {
|
||||||
|
$wm->rotateImage(new ImagickPixel('none'), $angle);
|
||||||
|
}
|
||||||
|
$wm->evaluateImage(Imagick::EVALUATE_MULTIPLY, $opacity / 100, Imagick::CHANNEL_ALPHA);
|
||||||
|
$wmW = $wm->getImageWidth();
|
||||||
|
$wmH = $wm->getImageHeight();
|
||||||
|
} else {
|
||||||
|
$wm = new Imagick();
|
||||||
|
$draw = new ImagickDraw();
|
||||||
|
$fontFile = $text_font;
|
||||||
|
if (!empty($fontFile) && !is_file($fontFile)) {
|
||||||
|
$base_font = basename($fontFile);
|
||||||
|
if (is_file(DIR_SYSTEM . 'library/font/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'library/font/' . $base_font;
|
||||||
|
} elseif (is_file(DIR_SYSTEM . 'fonts/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'fonts/' . $base_font;
|
||||||
|
} elseif (is_file(DIR_SYSTEM . 'font/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'font/' . $base_font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($fontFile) || !is_file($fontFile)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'library/font/Roboto-Regular.ttf';
|
||||||
|
}
|
||||||
|
if (is_file($fontFile)) {
|
||||||
|
$draw->setFont($fontFile);
|
||||||
|
}
|
||||||
|
$fontSize = max(12, $imgW * 0.04);
|
||||||
|
$draw->setFontSize($fontSize);
|
||||||
|
$colorHex = $text_color;
|
||||||
|
list($r, $g, $b) = $this->hex2rgb($colorHex);
|
||||||
|
$draw->setFillColor(new ImagickPixel('rgba(' . $r . ',' . $g . ',' . $b . ',' . ($opacity / 100) . ')'));
|
||||||
|
$draw->setGravity(Imagick::GRAVITY_CENTER);
|
||||||
|
$wm->newImage($imgW, $imgH, new ImagickPixel('transparent'));
|
||||||
|
$wm->annotateImage($draw, 0, 0, 0, $textVal);
|
||||||
|
if ($angle != 0) {
|
||||||
|
$wm->rotateImage(new ImagickPixel('none'), $angle);
|
||||||
|
}
|
||||||
|
$wmW = $wm->getImageWidth();
|
||||||
|
$wmH = $wm->getImageHeight();
|
||||||
|
}
|
||||||
|
$posX = 0; $posY = 0; $pad = 10;
|
||||||
|
if (in_array($position, array(1, 4, 7))) $posX = $pad;
|
||||||
|
elseif (in_array($position, array(2, 5, 8))) $posX = ($imgW - $wmW) / 2;
|
||||||
|
elseif (in_array($position, array(3, 6, 9))) $posX = $imgW - $wmW - $pad;
|
||||||
|
|
||||||
|
if (in_array($position, array(1, 2, 3))) $posY = $pad;
|
||||||
|
elseif (in_array($position, array(4, 5, 6))) $posY = ($imgH - $wmH) / 2;
|
||||||
|
elseif (in_array($position, array(7, 8, 9))) $posY = $imgH - $wmH - $pad;
|
||||||
|
|
||||||
|
$img->compositeImage($wm, Imagick::COMPOSITE_OVER, $posX, $posY);
|
||||||
|
$wm->clear(); $wm->destroy();
|
||||||
|
}
|
||||||
|
$img->writeImage($realCachedFile);
|
||||||
|
$img->clear(); $img->destroy();
|
||||||
|
} catch (Exception $e) {}
|
||||||
|
} else {
|
||||||
|
$ext = strtolower($this->mb_pathinfo($realCachedFile, PATHINFO_EXTENSION));
|
||||||
|
if ($ext === 'jpg' || $ext === 'jpeg') {
|
||||||
|
$img = @imagecreatefromjpeg($realCachedFile);
|
||||||
|
} elseif ($ext === 'png') {
|
||||||
|
$img = @imagecreatefrompng($realCachedFile);
|
||||||
|
} elseif ($ext === 'webp' && function_exists('imagecreatefromwebp')) {
|
||||||
|
$img = @imagecreatefromwebp($realCachedFile);
|
||||||
|
} else {
|
||||||
|
$img = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($img) {
|
||||||
|
if (function_exists('imagepalettetotruecolor') && !imageistruecolor($img)) {
|
||||||
|
imagepalettetotruecolor($img);
|
||||||
|
}
|
||||||
|
imagealphablending($img, true);
|
||||||
|
|
||||||
|
$imgW = imagesx($img);
|
||||||
|
$imgH = imagesy($img);
|
||||||
|
|
||||||
|
foreach ($matched_rules as $rule) {
|
||||||
|
$type = preg_replace('/[^a-z]/', '', $rule['type']);
|
||||||
|
$imagePathStr = str_replace(array('../', '..\\', ' '), '', (string)$rule['image']);
|
||||||
|
$imagePath = realpath(DIR_IMAGE . $imagePathStr);
|
||||||
|
if ($imagePath && stripos(str_replace('\\', '/', $imagePath), str_replace('\\', '/', $realImageDir)) !== 0) {
|
||||||
|
$imagePath = false;
|
||||||
|
}
|
||||||
|
$textVal = (string)$rule['text_val'];
|
||||||
|
$position = (int)$rule['position'];
|
||||||
|
$opacity = (int)$rule['opacity'];
|
||||||
|
$size_type = isset($rule['size_type']) ? $rule['size_type'] : 'original';
|
||||||
|
$size_percent = isset($rule['size_percent']) ? (int)$rule['size_percent'] : 20;
|
||||||
|
$angle = isset($rule['angle']) ? (int)$rule['angle'] : 0;
|
||||||
|
$corner_radius = isset($rule['corner_radius']) ? (int)$rule['corner_radius'] : 0;
|
||||||
|
$text_font = isset($rule['text_font']) ? (string)$rule['text_font'] : '';
|
||||||
|
$text_color = isset($rule['text_color']) ? (string)$rule['text_color'] : '#ffffff';
|
||||||
|
|
||||||
|
if ($type === 'image') {
|
||||||
|
if (!$imagePath || !is_file($imagePath)) continue;
|
||||||
|
}
|
||||||
|
if ($type === 'text' && empty($textVal)) continue;
|
||||||
|
|
||||||
|
if ($type === 'image') {
|
||||||
|
$wm = @imagecreatefrompng($imagePath);
|
||||||
|
if ($wm) {
|
||||||
|
if (function_exists('imagepalettetotruecolor') && !imageistruecolor($wm)) {
|
||||||
|
imagepalettetotruecolor($wm);
|
||||||
|
}
|
||||||
|
imagealphablending($wm, false);
|
||||||
|
imagesavealpha($wm, true);
|
||||||
|
|
||||||
|
$wmW = imagesx($wm);
|
||||||
|
$wmH = imagesy($wm);
|
||||||
|
|
||||||
|
$max_wm_w = (int)$this->config->get('module_img_opti_wm_max_width');
|
||||||
|
if ($max_wm_w <= 0) $max_wm_w = 800;
|
||||||
|
$max_wm_h = (int)$this->config->get('module_img_opti_wm_max_height');
|
||||||
|
if ($max_wm_h <= 0) $max_wm_h = 800;
|
||||||
|
|
||||||
|
if ($wmW > $max_wm_w || $wmH > $max_wm_h) {
|
||||||
|
$ratio = min($max_wm_w / $wmW, $max_wm_h / $wmH);
|
||||||
|
$new_wm_w = intval($wmW * $ratio);
|
||||||
|
$new_wm_h = intval($wmH * $ratio);
|
||||||
|
if ($new_wm_w > 0 && $new_wm_h > 0) {
|
||||||
|
$resized_wm = imagecreatetruecolor($new_wm_w, $new_wm_h);
|
||||||
|
imagealphablending($resized_wm, false);
|
||||||
|
imagesavealpha($resized_wm, true);
|
||||||
|
$transparent = imagecolorallocatealpha($resized_wm, 255, 255, 255, 127);
|
||||||
|
imagefill($resized_wm, 0, 0, $transparent);
|
||||||
|
imagecopyresampled($resized_wm, $wm, 0, 0, 0, 0, $new_wm_w, $new_wm_h, $wmW, $wmH);
|
||||||
|
imagedestroy($wm);
|
||||||
|
$wm = $resized_wm;
|
||||||
|
$wmW = $new_wm_w;
|
||||||
|
$wmH = $new_wm_h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($size_type === 'percent' && $size_percent > 0) {
|
||||||
|
$targetW = intval(($imgW * $size_percent) / 100);
|
||||||
|
$targetH = intval(($wmH / $wmW) * $targetW);
|
||||||
|
if ($targetW > 0 && $targetH > 0) {
|
||||||
|
$resized_wm = imagecreatetruecolor($targetW, $targetH);
|
||||||
|
imagealphablending($resized_wm, false);
|
||||||
|
imagesavealpha($resized_wm, true);
|
||||||
|
$transparent = imagecolorallocatealpha($resized_wm, 255, 255, 255, 127);
|
||||||
|
imagefill($resized_wm, 0, 0, $transparent);
|
||||||
|
imagecopyresampled($resized_wm, $wm, 0, 0, 0, 0, $targetW, $targetH, $wmW, $wmH);
|
||||||
|
imagedestroy($wm);
|
||||||
|
$wm = $resized_wm;
|
||||||
|
$wmW = $targetW;
|
||||||
|
$wmH = $targetH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($corner_radius > 0) {
|
||||||
|
$wm = $this->roundCornersGD($wm, $corner_radius);
|
||||||
|
}
|
||||||
|
if ($angle != 0) {
|
||||||
|
$gd_angle = -1 * $angle;
|
||||||
|
$transparent = imagecolorallocatealpha($wm, 255, 255, 255, 127);
|
||||||
|
$rotated = imagerotate($wm, $gd_angle, $transparent);
|
||||||
|
if ($rotated) {
|
||||||
|
imagealphablending($rotated, false);
|
||||||
|
imagesavealpha($rotated, true);
|
||||||
|
imagedestroy($wm);
|
||||||
|
$wm = $rotated;
|
||||||
|
$wmW = imagesx($wm);
|
||||||
|
$wmH = imagesy($wm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$posX = 0; $posY = 0; $pad = 10;
|
||||||
|
if (in_array($position, array(1, 4, 7))) $posX = $pad;
|
||||||
|
elseif (in_array($position, array(2, 5, 8))) $posX = ($imgW - $wmW) / 2;
|
||||||
|
elseif (in_array($position, array(3, 6, 9))) $posX = $imgW - $wmW - $pad;
|
||||||
|
|
||||||
|
if (in_array($position, array(1, 2, 3))) $posY = $pad;
|
||||||
|
elseif (in_array($position, array(4, 5, 6))) $posY = ($imgH - $wmH) / 2;
|
||||||
|
elseif (in_array($position, array(7, 8, 9))) $posY = $imgH - $wmH - $pad;
|
||||||
|
|
||||||
|
$this->imagecopymerge_alpha($img, $wm, $posX, $posY, 0, 0, $wmW, $wmH, $opacity);
|
||||||
|
imagedestroy($wm);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fontFile = $text_font;
|
||||||
|
if (!empty($fontFile) && !is_file($fontFile)) {
|
||||||
|
$base_font = basename($fontFile);
|
||||||
|
if (is_file(DIR_SYSTEM . 'library/font/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'library/font/' . $base_font;
|
||||||
|
} elseif (is_file(DIR_SYSTEM . 'fonts/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'fonts/' . $base_font;
|
||||||
|
} elseif (is_file(DIR_SYSTEM . 'font/' . $base_font)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'font/' . $base_font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($fontFile) || !is_file($fontFile)) {
|
||||||
|
$fontFile = DIR_SYSTEM . 'library/font/Roboto-Regular.ttf';
|
||||||
|
}
|
||||||
|
if (!is_file($fontFile)) {
|
||||||
|
$open_basedir = ini_get('open_basedir');
|
||||||
|
$fallback_dirs = array(
|
||||||
|
DIR_SYSTEM . 'library/font/',
|
||||||
|
DIR_SYSTEM . 'fonts/',
|
||||||
|
DIR_SYSTEM . 'font/',
|
||||||
|
DIR_CATALOG . 'view/theme/default/fonts/'
|
||||||
|
);
|
||||||
|
if (DIRECTORY_SEPARATOR === '/') {
|
||||||
|
if (!$open_basedir) {
|
||||||
|
$fallback_dirs[] = '/usr/share/fonts/truetype/dejavu/';
|
||||||
|
$fallback_dirs[] = '/usr/share/fonts/truetype/liberation/';
|
||||||
|
$fallback_dirs[] = '/usr/share/fonts/truetype/freefont/';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$open_basedir) {
|
||||||
|
$fallback_dirs[] = 'C:/Windows/Fonts/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($fallback_dirs as $f_dir) {
|
||||||
|
if (@is_dir($f_dir)) {
|
||||||
|
$glob = @glob($f_dir . '*.ttf');
|
||||||
|
if ($glob) {
|
||||||
|
$fontFile = $glob[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_file($fontFile)) {
|
||||||
|
$fontSize = max(3, intval($imgW / 50));
|
||||||
|
$fontWidth = imagefontwidth($fontSize) * strlen($textVal);
|
||||||
|
$fontHeight = imagefontheight($fontSize);
|
||||||
|
$posX = 0; $posY = 0; $pad = 10;
|
||||||
|
if (in_array($position, array(1, 4, 7))) $posX = $pad;
|
||||||
|
elseif (in_array($position, array(2, 5, 8))) $posX = ($imgW - $fontWidth) / 2;
|
||||||
|
elseif (in_array($position, array(3, 6, 9))) $posX = $imgW - $fontWidth - $pad;
|
||||||
|
|
||||||
|
if (in_array($position, array(1, 2, 3))) $posY = $pad;
|
||||||
|
elseif (in_array($position, array(4, 5, 6))) $posY = ($imgH - $fontHeight) / 2;
|
||||||
|
elseif (in_array($position, array(7, 8, 9))) $posY = $imgH - $fontHeight - $pad;
|
||||||
|
|
||||||
|
$alpha = intval(127 - (127 * ($opacity / 100)));
|
||||||
|
$color = imagecolorallocatealpha($img, 255, 255, 255, $alpha);
|
||||||
|
imagestring($img, $fontSize, $posX, $posY, $textVal, $color);
|
||||||
|
} else {
|
||||||
|
$fontSize = max(10, intval($imgW / 25));
|
||||||
|
$colorHex = $text_color;
|
||||||
|
list($r, $g, $b) = $this->hex2rgb($colorHex);
|
||||||
|
$alpha = intval(127 - (127 * ($opacity / 100)));
|
||||||
|
$color = imagecolorallocatealpha($img, $r, $g, $b, $alpha);
|
||||||
|
|
||||||
|
$bbox = imagettfbbox($fontSize, $angle, $fontFile, $textVal);
|
||||||
|
$textW = abs($bbox[4] - $bbox[0]);
|
||||||
|
$textH = abs($bbox[5] - $bbox[1]);
|
||||||
|
|
||||||
|
$posX = 0; $posY = 0; $pad = 10;
|
||||||
|
if (in_array($position, array(1, 4, 7))) $posX = $pad;
|
||||||
|
elseif (in_array($position, array(2, 5, 8))) $posX = ($imgW - $textW) / 2;
|
||||||
|
elseif (in_array($position, array(3, 6, 9))) $posX = $imgW - $textW - $pad;
|
||||||
|
|
||||||
|
if (in_array($position, array(1, 2, 3))) $posY = $pad + $textH;
|
||||||
|
elseif (in_array($position, array(4, 5, 6))) $posY = ($imgH - $textH) / 2 + $textH;
|
||||||
|
elseif (in_array($position, array(7, 8, 9))) $posY = $imgH - $pad;
|
||||||
|
|
||||||
|
imagettftext($img, $fontSize, $angle, $posX, $posY, $color, $fontFile, $textVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ext === 'png' || $ext === 'webp') {
|
||||||
|
imagealphablending($img, false);
|
||||||
|
imagesavealpha($img, true);
|
||||||
|
}
|
||||||
|
if ($ext === 'jpg' || $ext === 'jpeg') { imagejpeg($img, $realCachedFile, 90); }
|
||||||
|
elseif ($ext === 'png') { imagepng($img, $realCachedFile, 9); }
|
||||||
|
elseif ($ext === 'webp') { imagewebp($img, $realCachedFile, 90); }
|
||||||
|
imagedestroy($img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hex2rgb($hex) {
|
||||||
|
$hex = str_replace('#', '', $hex);
|
||||||
|
if (strlen($hex) == 3) {
|
||||||
|
$r = hexdec(substr($hex, 0, 1) . substr($hex, 0, 1));
|
||||||
|
$g = hexdec(substr($hex, 1, 1) . substr($hex, 1, 1));
|
||||||
|
$b = hexdec(substr($hex, 2, 1) . substr($hex, 2, 1));
|
||||||
|
} else {
|
||||||
|
$r = hexdec(substr($hex, 0, 2));
|
||||||
|
$g = hexdec(substr($hex, 2, 2));
|
||||||
|
$b = hexdec(substr($hex, 4, 2));
|
||||||
|
}
|
||||||
|
return array($r, $g, $b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function roundCornersGD($im, $radius) {
|
||||||
|
$w = imagesx($im);
|
||||||
|
$h = imagesy($im);
|
||||||
|
$new_im = imagecreatetruecolor($w, $h);
|
||||||
|
imagealphablending($new_im, false);
|
||||||
|
imagesavealpha($new_im, true);
|
||||||
|
$transparent = imagecolorallocatealpha($new_im, 255, 255, 255, 127);
|
||||||
|
imagefill($new_im, 0, 0, $transparent);
|
||||||
|
for ($x = 0; $x < $w; $x++) {
|
||||||
|
for ($y = 0; $y < $h; $y++) {
|
||||||
|
$rgba = imagecolorat($im, $x, $y);
|
||||||
|
$in_corner = false;
|
||||||
|
if ($x < $radius && $y < $radius) {
|
||||||
|
if (pow($radius - $x, 2) + pow($radius - $y, 2) > pow($radius, 2)) $in_corner = true;
|
||||||
|
} elseif ($x >= $w - $radius && $y < $radius) {
|
||||||
|
if (pow($x - ($w - $radius - 1), 2) + pow($radius - $y, 2) > pow($radius, 2)) $in_corner = true;
|
||||||
|
} elseif ($x < $radius && $y >= $h - $radius) {
|
||||||
|
if (pow($radius - $x, 2) + pow($y - ($h - $radius - 1), 2) > pow($radius, 2)) $in_corner = true;
|
||||||
|
} elseif ($x >= $w - $radius && $y >= $h - $radius) {
|
||||||
|
if (pow($x - ($w - $radius - 1), 2) + pow($y - ($h - $radius - 1), 2) > pow($radius, 2)) $in_corner = true;
|
||||||
|
}
|
||||||
|
if ($in_corner) {
|
||||||
|
imagesetpixel($new_im, $x, $y, $transparent);
|
||||||
|
} else {
|
||||||
|
imagesetpixel($new_im, $x, $y, $rgba);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imagedestroy($im);
|
||||||
|
return $new_im;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct) {
|
||||||
|
$opacity = $pct / 100;
|
||||||
|
for ($x = 0; $x < $src_w; $x++) {
|
||||||
|
for ($y = 0; $y < $src_h; $y++) {
|
||||||
|
$src_color_idx = imagecolorat($src_im, $src_x + $x, $src_y + $y);
|
||||||
|
$src_color = imagecolorsforindex($src_im, $src_color_idx);
|
||||||
|
$src_alpha = (127 - $src_color['alpha']) / 127;
|
||||||
|
$src_alpha = $src_alpha * $opacity;
|
||||||
|
if ($src_alpha <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$dst_x_pos = $dst_x + $x;
|
||||||
|
$dst_y_pos = $dst_y + $y;
|
||||||
|
if ($dst_x_pos < 0 || $dst_x_pos >= imagesx($dst_im) || $dst_y_pos < 0 || $dst_y_pos >= imagesy($dst_im)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$dst_color_idx = imagecolorat($dst_im, $dst_x_pos, $dst_y_pos);
|
||||||
|
$dst_color = imagecolorsforindex($dst_im, $dst_color_idx);
|
||||||
|
$dst_alpha = (127 - $dst_color['alpha']) / 127;
|
||||||
|
$out_alpha = $src_alpha + $dst_alpha * (1 - $src_alpha);
|
||||||
|
if ($out_alpha > 0) {
|
||||||
|
$out_r = ($src_color['red'] * $src_alpha + $dst_color['red'] * $dst_alpha * (1 - $src_alpha)) / $out_alpha;
|
||||||
|
$out_g = ($src_color['green'] * $src_alpha + $dst_color['green'] * $dst_alpha * (1 - $src_alpha)) / $out_alpha;
|
||||||
|
$out_b = ($src_color['blue'] * $src_alpha + $dst_color['blue'] * $dst_alpha * (1 - $src_alpha)) / $out_alpha;
|
||||||
|
$out_alpha_val = 127 - round($out_alpha * 127);
|
||||||
|
} else {
|
||||||
|
$out_r = 0;
|
||||||
|
$out_g = 0;
|
||||||
|
$out_b = 0;
|
||||||
|
$out_alpha_val = 127;
|
||||||
|
}
|
||||||
|
$out_r = max(0, min(255, round($out_r)));
|
||||||
|
$out_g = max(0, min(255, round($out_g)));
|
||||||
|
$out_b = max(0, min(255, round($out_b)));
|
||||||
|
$out_alpha_val = max(0, min(127, $out_alpha_val));
|
||||||
|
$new_color = imagecolorallocatealpha($dst_im, $out_r, $out_g, $out_b, $out_alpha_val);
|
||||||
|
if ($new_color === false) {
|
||||||
|
$new_color = imagecolorresolvealpha($dst_im, $out_r, $out_g, $out_b, $out_alpha_val);
|
||||||
|
}
|
||||||
|
imagesetpixel($dst_im, $dst_x_pos, $dst_y_pos, $new_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mb_pathinfo($path, $options = null) {
|
||||||
|
$ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
|
||||||
|
$path = str_replace('\\', '/', $path);
|
||||||
|
$last_slash = strrpos($path, '/');
|
||||||
|
if ($last_slash !== false) {
|
||||||
|
$ret['dirname'] = substr($path, 0, $last_slash);
|
||||||
|
$basename = substr($path, $last_slash + 1);
|
||||||
|
} else {
|
||||||
|
$basename = $path;
|
||||||
|
}
|
||||||
|
$ret['basename'] = $basename;
|
||||||
|
$last_dot = strrpos($basename, '.');
|
||||||
|
if ($last_dot !== false) {
|
||||||
|
$ret['filename'] = substr($basename, 0, $last_dot);
|
||||||
|
$ret['extension'] = substr($basename, $last_dot + 1);
|
||||||
|
} else {
|
||||||
|
$ret['filename'] = $basename;
|
||||||
|
}
|
||||||
|
if ($options === PATHINFO_DIRNAME) return $ret['dirname'];
|
||||||
|
if ($options === PATHINFO_BASENAME) return $ret['basename'];
|
||||||
|
if ($options === PATHINFO_EXTENSION) return $ret['extension'];
|
||||||
|
if ($options === PATHINFO_FILENAME) return $ret['filename'];
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkLicense($key) {
|
||||||
|
$host = explode(':', str_replace(array('http://', 'https://', 'www.'), '', strtolower($this->request->server['HTTP_HOST'])))[0];
|
||||||
|
if ($host === 'localhost' || $host === '127.0.0.1') return true;
|
||||||
|
if (empty($key)) return false;
|
||||||
|
$parts = explode('.', $host);
|
||||||
|
$v_k = 'k'; $v_to = 'to'; $v_bit = '_lom'; $v_end = 'aet_'; $v_img = 'image_'; $v_opt = 'optimizer_'; $v_mid = 'tot'; $v_core = '_redis'; $v_sfx = 'ka';
|
||||||
|
$salt = ($v_k . $v_to . $v_bit) . ($v_end . $v_img . $v_opt . $v_mid) . ($v_core . $v_sfx);
|
||||||
|
while(count($parts) >= 2) {
|
||||||
|
$domain = implode('.', $parts);
|
||||||
|
if ($key === strtoupper(implode('-', str_split(substr(md5($domain . $salt), 0, 16), 4)))) return true;
|
||||||
|
array_shift($parts);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mb_basename($path) {
|
||||||
|
$path = str_replace('\\', '/', $path);
|
||||||
|
$parts = explode('/', $path);
|
||||||
|
return end($parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,47 @@
|
||||||
|
DROP TABLE IF EXISTS `elfinder_file`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `elfinder_file` (
|
||||||
|
`id` int(7) unsigned NOT NULL auto_increment,
|
||||||
|
`parent_id` int(7) unsigned NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`content` longblob NOT NULL,
|
||||||
|
`size` int(10) unsigned NOT NULL default '0',
|
||||||
|
`mtime` int(10) unsigned NOT NULL default '0',
|
||||||
|
`mime` varchar(256) NOT NULL default 'unknown',
|
||||||
|
`read` enum('1', '0') NOT NULL default '1',
|
||||||
|
`write` enum('1', '0') NOT NULL default '1',
|
||||||
|
`locked` enum('1', '0') NOT NULL default '0',
|
||||||
|
`hidden` enum('1', '0') NOT NULL default '0',
|
||||||
|
`width` int(5) NOT NULL default '0',
|
||||||
|
`height` int(5) NOT NULL default '0',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `parent_name` (`parent_id`, `name`),
|
||||||
|
KEY `parent_id` (`parent_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `elfinder_file`
|
||||||
|
(`id`, `parent_id`, `name`, `content`, `size`, `mtime`, `mime`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES
|
||||||
|
('1' , '0', 'DATABASE', '', '0', '0','directory', '1', '1', '0', '0', '0', '0');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `elfinder_trash`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `elfinder_trash` (
|
||||||
|
`id` int(7) unsigned NOT NULL auto_increment,
|
||||||
|
`parent_id` int(7) unsigned NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`content` longblob NOT NULL,
|
||||||
|
`size` int(10) unsigned NOT NULL default '0',
|
||||||
|
`mtime` int(10) unsigned NOT NULL default '0',
|
||||||
|
`mime` varchar(256) NOT NULL default 'unknown',
|
||||||
|
`read` enum('1', '0') NOT NULL default '1',
|
||||||
|
`write` enum('1', '0') NOT NULL default '1',
|
||||||
|
`locked` enum('1', '0') NOT NULL default '0',
|
||||||
|
`hidden` enum('1', '0') NOT NULL default '0',
|
||||||
|
`width` int(5) NOT NULL default '0',
|
||||||
|
`height` int(5) NOT NULL default '0',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `parent_name` (`parent_id`, `name`),
|
||||||
|
KEY `parent_id` (`parent_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `elfinder_trash`
|
||||||
|
(`id`, `parent_id`, `name`, `content`, `size`, `mtime`, `mime`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES
|
||||||
|
('1' , '0', 'DB Trash', '', '0', '0','directory', '1', '1', '0', '0', '0', '0');
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('ELFINDER_PHP_ROOT_PATH', dirname(__FILE__));
|
||||||
|
|
||||||
|
function elFinderAutoloader($name)
|
||||||
|
{
|
||||||
|
$map = array(
|
||||||
|
'elFinder' => 'elFinder.class.php',
|
||||||
|
'elFinderConnector' => 'elFinderConnector.class.php',
|
||||||
|
'elFinderEditor' => 'editors/editor.php',
|
||||||
|
'elFinderLibGdBmp' => 'libs/GdBmp.php',
|
||||||
|
'elFinderPlugin' => 'elFinderPlugin.php',
|
||||||
|
'elFinderPluginAutoResize' => 'plugins/AutoResize/plugin.php',
|
||||||
|
'elFinderPluginAutoRotate' => 'plugins/AutoRotate/plugin.php',
|
||||||
|
'elFinderPluginNormalizer' => 'plugins/Normalizer/plugin.php',
|
||||||
|
'elFinderPluginSanitizer' => 'plugins/Sanitizer/plugin.php',
|
||||||
|
'elFinderPluginWatermark' => 'plugins/Watermark/plugin.php',
|
||||||
|
'elFinderSession' => 'elFinderSession.php',
|
||||||
|
'elFinderSessionInterface' => 'elFinderSessionInterface.php',
|
||||||
|
'elFinderVolumeDriver' => 'elFinderVolumeDriver.class.php',
|
||||||
|
'elFinderVolumeDropbox2' => 'elFinderVolumeDropbox2.class.php',
|
||||||
|
'elFinderVolumeFTP' => 'elFinderVolumeFTP.class.php',
|
||||||
|
'elFinderVolumeFlysystemGoogleDriveCache' => 'elFinderFlysystemGoogleDriveNetmount.php',
|
||||||
|
'elFinderVolumeFlysystemGoogleDriveNetmount' => 'elFinderFlysystemGoogleDriveNetmount.php',
|
||||||
|
'elFinderVolumeGoogleDrive' => 'elFinderVolumeGoogleDrive.class.php',
|
||||||
|
'elFinderVolumeGroup' => 'elFinderVolumeGroup.class.php',
|
||||||
|
'elFinderVolumeLocalFileSystem' => 'elFinderVolumeLocalFileSystem.class.php',
|
||||||
|
'elFinderVolumeMySQL' => 'elFinderVolumeMySQL.class.php',
|
||||||
|
'elFinderVolumeTrash' => 'elFinderVolumeTrash.class.php',
|
||||||
|
);
|
||||||
|
if (isset($map[$name])) {
|
||||||
|
return include_once(ELFINDER_PHP_ROOT_PATH . '/' . $map[$name]);
|
||||||
|
}
|
||||||
|
$prefix = substr($name, 0, 14);
|
||||||
|
if (substr($prefix, 0, 8) === 'elFinder') {
|
||||||
|
if ($prefix === 'elFinderVolume') {
|
||||||
|
$file = ELFINDER_PHP_ROOT_PATH . '/' . $name . '.class.php';
|
||||||
|
return (is_file($file) && include_once($file));
|
||||||
|
} else if ($prefix === 'elFinderPlugin') {
|
||||||
|
$file = ELFINDER_PHP_ROOT_PATH . '/plugins/' . substr($name, 14) . '/plugin.php';
|
||||||
|
return (is_file($file) && include_once($file));
|
||||||
|
} else if ($prefix === 'elFinderEditor') {
|
||||||
|
$file = ELFINDER_PHP_ROOT_PATH . '/editors/' . substr($name, 14) . '/editor.php';
|
||||||
|
return (is_file($file) && include_once($file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, '5.3', '<')) {
|
||||||
|
spl_autoload_register('elFinderAutoloader');
|
||||||
|
} else {
|
||||||
|
spl_autoload_register('elFinderAutoloader', true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,433 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
error_reporting(0); // Set E_ALL for debuging
|
||||||
|
|
||||||
|
// // Optional exec path settings (Default is called with command name only)
|
||||||
|
// define('ELFINDER_TAR_PATH', '/PATH/TO/tar');
|
||||||
|
// define('ELFINDER_GZIP_PATH', '/PATH/TO/gzip');
|
||||||
|
// define('ELFINDER_BZIP2_PATH', '/PATH/TO/bzip2');
|
||||||
|
// define('ELFINDER_XZ_PATH', '/PATH/TO/xz');
|
||||||
|
// define('ELFINDER_ZIP_PATH', '/PATH/TO/zip');
|
||||||
|
// define('ELFINDER_UNZIP_PATH', '/PATH/TO/unzip');
|
||||||
|
// define('ELFINDER_RAR_PATH', '/PATH/TO/rar');
|
||||||
|
// define('ELFINDER_UNRAR_PATH', '/PATH/TO/unrar');
|
||||||
|
// define('ELFINDER_7Z_PATH', '/PATH/TO/7za');
|
||||||
|
// define('ELFINDER_CONVERT_PATH', '/PATH/TO/convert');
|
||||||
|
// define('ELFINDER_IDENTIFY_PATH', '/PATH/TO/identify');
|
||||||
|
// define('ELFINDER_EXIFTRAN_PATH', '/PATH/TO/exiftran');
|
||||||
|
// define('ELFINDER_JPEGTRAN_PATH', '/PATH/TO/jpegtran');
|
||||||
|
// define('ELFINDER_FFMPEG_PATH', '/PATH/TO/ffmpeg');
|
||||||
|
|
||||||
|
// define('ELFINDER_CONNECTOR_URL', 'URL to this connector script'); // see elFinder::getConnectorUrl()
|
||||||
|
|
||||||
|
// define('ELFINDER_DEBUG_ERRORLEVEL', -1); // Error reporting level of debug mode
|
||||||
|
|
||||||
|
// // To Enable(true) handling of PostScript files by ImageMagick
|
||||||
|
// // It is disabled by default as a countermeasure
|
||||||
|
// // of Ghostscript multiple -dSAFER sandbox bypass vulnerabilities
|
||||||
|
// // see https://www.kb.cert.org/vuls/id/332928
|
||||||
|
// define('ELFINDER_IMAGEMAGICK_PS', true);
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // load composer autoload before load elFinder autoload If you need composer
|
||||||
|
// // You need to run the composer command in the php directory.
|
||||||
|
is_readable('./vendor/autoload.php') && require './vendor/autoload.php';
|
||||||
|
|
||||||
|
// // elFinder autoload
|
||||||
|
require './autoload.php';
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Enable FTP connector netmount
|
||||||
|
elFinder::$netDrivers['ftp'] = 'FTP';
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Dropbox network mount
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require kunalvarma05/dropbox-php-sdk` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['dropbox2'] = 'Dropbox2';
|
||||||
|
// // Dropbox2 Netmount driver need next two settings. You can get at https://www.dropbox.com/developers/apps
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=dropbox2&host=1"
|
||||||
|
// define('ELFINDER_DROPBOX_APPKEY', '');
|
||||||
|
// define('ELFINDER_DROPBOX_APPSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Google Drive network mount
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require google/apiclient:^2.0` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['googledrive'] = 'GoogleDrive';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://console.developers.google.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=googledrive&host=1"
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTSECRET', '');
|
||||||
|
// // Required case when Google API is NOT added via composer
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT', '/path/to/google-api-php-client/vendor/autoload.php');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Google Drive network mount with Flysystem
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require nao-pon/flysystem-google-drive:~1.1 nao-pon/elfinder-flysystem-driver-ext` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['googledrive'] = 'FlysystemGoogleDriveNetmount';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://console.developers.google.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=googledrive&host=1"
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTSECRET', '');
|
||||||
|
// // And "php/.tmp" directory must exist and be writable by PHP.
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for One Drive network mount
|
||||||
|
// // * cURL PHP extension required
|
||||||
|
// // * HTTP server PATH_INFO supports required
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['onedrive'] = 'OneDrive';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://dev.onedrive.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL/netmount/onedrive/1"
|
||||||
|
// define('ELFINDER_ONEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_ONEDRIVE_CLIENTSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Box network mount
|
||||||
|
// // * cURL PHP extension required
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['box'] = 'Box';
|
||||||
|
// // Box Netmount driver need next two settings. You can get at https://developer.box.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=box&host=1"
|
||||||
|
// define('ELFINDER_BOX_CLIENTID', '');
|
||||||
|
// define('ELFINDER_BOX_CLIENTSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
|
||||||
|
// // Zoho Office Editor APIKey
|
||||||
|
// // https://www.zoho.com/docs/help/office-apis.html
|
||||||
|
// define('ELFINDER_ZOHO_OFFICE_APIKEY', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Online converter (online-convert.com) APIKey
|
||||||
|
// // https://apiv2.online-convert.com/docs/getting_started/api_key.html
|
||||||
|
// define('ELFINDER_ONLINE_CONVERT_APIKEY', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Zip Archive editor
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require nao-pon/elfinder-flysystem-ziparchive-netmount` on php directory
|
||||||
|
// define('ELFINDER_DISABLE_ZIPEDITOR', false); // set `true` to disable zip editor
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple function to demonstrate how to control file access using "accessControl" callback.
|
||||||
|
* This method will disable accessing files/folders starting from '.' (dot)
|
||||||
|
*
|
||||||
|
* @param string $attr attribute name (read|write|locked|hidden)
|
||||||
|
* @param string $path absolute file path
|
||||||
|
* @param string $data value of volume option `accessControlData`
|
||||||
|
* @param object $volume elFinder volume driver object
|
||||||
|
* @param bool|null $isDir path is directory (true: directory, false: file, null: unknown)
|
||||||
|
* @param string $relpath file path relative to volume root directory started with directory separator
|
||||||
|
* @return bool|null
|
||||||
|
**/
|
||||||
|
function access($attr, $path, $data, $volume, $isDir, $relpath) {
|
||||||
|
$basename = basename($path);
|
||||||
|
return $basename[0] === '.' // if file/folder begins with '.' (dot)
|
||||||
|
&& strlen($relpath) !== 1 // but with out volume root
|
||||||
|
? !($attr == 'read' || $attr == 'write') // set read+write to false, other (locked+hidden) set to true
|
||||||
|
: null; // else elFinder decide it itself
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple debug function
|
||||||
|
* Usage: debug($anyVal[, $anyVal2 ...]);
|
||||||
|
*/
|
||||||
|
function debug() {
|
||||||
|
$arg = func_get_args();
|
||||||
|
ob_start();
|
||||||
|
foreach($arg as $v) {
|
||||||
|
var_dump($v);
|
||||||
|
}
|
||||||
|
$o = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
file_put_contents('.debug.txt', $o, FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple logger function.
|
||||||
|
* Demonstrate how to work with elFinder event api.
|
||||||
|
*
|
||||||
|
* @package elFinder
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
**/
|
||||||
|
class elFinderSimpleLogger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log file path
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
**/
|
||||||
|
protected $file = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
**/
|
||||||
|
public function __construct($path)
|
||||||
|
{
|
||||||
|
$this->file = $path;
|
||||||
|
$dir = dirname($path);
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
mkdir($dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create log record
|
||||||
|
*
|
||||||
|
* @param string $cmd command name
|
||||||
|
* @param array $result command result
|
||||||
|
* @param array $args command arguments from client
|
||||||
|
* @param elFinder $elfinder elFinder instance
|
||||||
|
* @param elFinderVolumeDriver $volume current volume driver instance
|
||||||
|
* @return void|true
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
**/
|
||||||
|
public function log($cmd, $result, $args, $elfinder, $volume)
|
||||||
|
{
|
||||||
|
$log = $cmd.' ['.date('d.m H:s')."]\n";
|
||||||
|
|
||||||
|
if (!empty($result['error'])) {
|
||||||
|
$log .= "\tERROR: ".implode(' ', $result['error'])."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result['warning'])) {
|
||||||
|
$log .= "\tWARNING: ".implode(' ', $result['warning'])."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result['removed'])) {
|
||||||
|
foreach ($result['removed'] as $file) {
|
||||||
|
// removed file contain additional field "realpath"
|
||||||
|
$log .= "\tREMOVED: ".$file['realpath']."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result['added'])) {
|
||||||
|
foreach ($result['added'] as $file) {
|
||||||
|
$log .= "\tADDED: ".$elfinder->realpath($file['hash'])."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result['changed'])) {
|
||||||
|
foreach ($result['changed'] as $file) {
|
||||||
|
$log .= "\tCHANGED: ".$elfinder->realpath($file['hash'])."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->write($log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write log into file
|
||||||
|
*
|
||||||
|
* @param string $log log record
|
||||||
|
* @return void
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
**/
|
||||||
|
protected function write($log)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (($fp = @fopen($this->file, 'a'))) {
|
||||||
|
fwrite($fp, $log."\n");
|
||||||
|
fclose($fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // END class
|
||||||
|
// Make logger instance
|
||||||
|
$logger = new elFinderSimpleLogger('.log.txt');
|
||||||
|
|
||||||
|
// Documentation for connector options:
|
||||||
|
// https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options
|
||||||
|
$opts = array(
|
||||||
|
'debug' => true, // enable debug mode
|
||||||
|
'roots' => array(
|
||||||
|
// Items volume
|
||||||
|
array(
|
||||||
|
'driver' => 'LocalFileSystem', // driver for accessing file system (REQUIRED)
|
||||||
|
'path' => '../files/', // path to files (REQUIRED)
|
||||||
|
'URL' => dirname($_SERVER['PHP_SELF']) . '/../files/', // URL to files (REQUIRED)
|
||||||
|
'trashHash' => 't1_Lw', // elFinder's hash of trash folder
|
||||||
|
'winHashFix' => DIRECTORY_SEPARATOR !== '/', // to make hash same to Linux one on windows too
|
||||||
|
'uploadDeny' => array('all'), // All Mimetypes not allowed to upload
|
||||||
|
'uploadAllow' => array('image/x-ms-bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/x-icon', 'text/plain'), // Mimetype `image` and `text/plain` allowed to upload
|
||||||
|
'uploadOrder' => array('deny', 'allow'), // allowed Mimetype `image` and `text/plain` only
|
||||||
|
'accessControl' => 'access', // disable and hide dot starting files (OPTIONAL)
|
||||||
|
'attributes' => array( // additional thumbnail directories
|
||||||
|
'pattern' => '~^/\.tmb(?:Cloud|Netmount)$~',
|
||||||
|
'read' => false,
|
||||||
|
'write' => false,
|
||||||
|
'locked' => true,
|
||||||
|
'hidden' => true ),
|
||||||
|
),
|
||||||
|
// Trash volume
|
||||||
|
array(
|
||||||
|
'id' => '1',
|
||||||
|
'driver' => 'Trash',
|
||||||
|
'path' => '../files/.trash/',
|
||||||
|
'tmbURL' => dirname($_SERVER['PHP_SELF']) . '/../files/.trash/.tmb/',
|
||||||
|
'winHashFix' => DIRECTORY_SEPARATOR !== '/', // to make hash same to Linux one on windows too
|
||||||
|
'uploadDeny' => array('all'), // Recomend the same settings as the original volume that uses the trash
|
||||||
|
'uploadAllow' => array('image/x-ms-bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/x-icon', 'text/plain'), // Same as above
|
||||||
|
'uploadOrder' => array('deny', 'allow'), // Same as above
|
||||||
|
'accessControl' => 'access', // Same as above
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// some bind functions
|
||||||
|
'bind' => array(
|
||||||
|
// enable logger
|
||||||
|
// '*' => array($logger, 'log'),
|
||||||
|
'mkdir mkfile rename duplicate upload rm paste' => array($logger, 'log'),
|
||||||
|
// enable plugins
|
||||||
|
'archive.pre ls.pre mkdir.pre mkfile.pre rename.pre upload.pre' => array(
|
||||||
|
'Plugin.Normalizer.cmdPreprocess',
|
||||||
|
'Plugin.Sanitizer.cmdPreprocess'
|
||||||
|
),
|
||||||
|
'upload.presave' => array(
|
||||||
|
'Plugin.AutoRotate.onUpLoadPreSave',
|
||||||
|
'Plugin.AutoResize.onUpLoadPreSave',
|
||||||
|
'Plugin.Watermark.onUpLoadPreSave',
|
||||||
|
'Plugin.Normalizer.onUpLoadPreSave',
|
||||||
|
'Plugin.Sanitizer.onUpLoadPreSave',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// volume options of netmount volumes
|
||||||
|
'optionsNetVolumes' => array(
|
||||||
|
'*' => array( // "*" is all of netmount volumes
|
||||||
|
'tmbURL' => dirname($_SERVER['PHP_SELF']) . '/../files/.tmbNetmount/',
|
||||||
|
'tmbPath' => '../files/.tmbNetmount',
|
||||||
|
'tmbGcMaxlifeHour' => 1, // 1 hour
|
||||||
|
'tmbGcPercentage' => 10, // 10 execute / 100 tmb querys
|
||||||
|
'plugin' => array(
|
||||||
|
'AutoResize' => array(
|
||||||
|
'enable' => false
|
||||||
|
),
|
||||||
|
'Watermark' => array(
|
||||||
|
'enable' => false
|
||||||
|
),
|
||||||
|
'Normalizer' => array(
|
||||||
|
'enable' => false
|
||||||
|
),
|
||||||
|
'Sanitizer' => array(
|
||||||
|
'enable' => false
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extended other volume types
|
||||||
|
// To get an access token or refresh token, see the elFinder wiki.
|
||||||
|
// https://github.com/Studio-42/elFinder/wiki/How-to-get-OAuth-token
|
||||||
|
|
||||||
|
// Thumbnail settings for cloud volumes
|
||||||
|
$tmbConfig = array(
|
||||||
|
'tmbPath' => '../files/.tmbCloud',
|
||||||
|
'tmbURL' => dirname($_SERVER['PHP_SELF']) . '/../files/.tmbCloud/',
|
||||||
|
'tmbGcMaxlifeHour' => 2160, // 90 days
|
||||||
|
'tmbGcPercentage' => 5, // 5 execute / 100 tmb querys
|
||||||
|
);
|
||||||
|
|
||||||
|
// MySQL config
|
||||||
|
$mySqlConfig = array(
|
||||||
|
'path' => 1,
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'user' => '', // @String DB user name
|
||||||
|
'pass' => '', // @String DB user password
|
||||||
|
'db' => '', // @String Database name
|
||||||
|
'uploadMaxSize' => '10M', // It should be less than "max_allowed_packet" value of MySQL setting
|
||||||
|
);
|
||||||
|
// MySQL volume
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, $mySqlConfig, array(
|
||||||
|
'driver' => 'MySQL',
|
||||||
|
'trashHash' => 'tm1_MQ', // set trash to MySQL trash (tm1_) 1 (MQ)
|
||||||
|
'files_table' => 'elfinder_file',
|
||||||
|
));
|
||||||
|
// MySQL trash volume
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, $mySqlConfig, array(
|
||||||
|
'id' => '1', // volume id became "tm1_"
|
||||||
|
'alias' => 'DB Trash',
|
||||||
|
'driver' => 'TrashMySQL',
|
||||||
|
'files_table' => 'elfinder_trash',
|
||||||
|
));
|
||||||
|
|
||||||
|
// Volume group
|
||||||
|
$opts['roots'][] = array(
|
||||||
|
'id' => '1', // volume id became "g1_"
|
||||||
|
'alias' => 'CloudVolumes',
|
||||||
|
'driver' => 'Group',
|
||||||
|
'rootCssClass' => 'elfinder-navbar-root-network' // set volume icon
|
||||||
|
);
|
||||||
|
|
||||||
|
// FTP volume
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'FTP',
|
||||||
|
'host' => 'ftp.jaist.ac.jp',
|
||||||
|
'user' => 'anonymous',
|
||||||
|
'path' => '/',
|
||||||
|
'owner' => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
// To enable the following cloud volumes, first complete the steps
|
||||||
|
// for enabling each network-mounted volume described earlier in this file.
|
||||||
|
|
||||||
|
// Box volume
|
||||||
|
// Require constant "ELFINDER_BOX_CLIENTID" and "ELFINDER_BOX_CLIENTSECRET"
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'Box',
|
||||||
|
'path' => '/', // or folder id as root
|
||||||
|
'accessToken' => '', // @JSON String access token including refresh token
|
||||||
|
));
|
||||||
|
|
||||||
|
// Dropbox volume
|
||||||
|
// Require constant "ELFINDER_DROPBOX_APPKEY" and "ELFINDER_DROPBOX_APPSECRET"
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'Dropbox2',
|
||||||
|
'path' => '/', // or folder path as root
|
||||||
|
'access_token' => '', // @String your access token
|
||||||
|
));
|
||||||
|
|
||||||
|
// GoogleDrive volume with refresh token
|
||||||
|
// Require constant "ELFINDER_GOOGLEDRIVE_CLIENTID" and "ELFINDER_GOOGLEDRIVE_CLIENTSECRET"
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'GoogleDrive',
|
||||||
|
'path' => '/', // or folder id as root
|
||||||
|
'refresh_token' => '', // @String your refresh token
|
||||||
|
));
|
||||||
|
|
||||||
|
// GoogleDrive volume with service account
|
||||||
|
// Require constant "ELFINDER_GOOGLEDRIVE_CLIENTID" and "ELFINDER_GOOGLEDRIVE_CLIENTSECRET"
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'GoogleDrive',
|
||||||
|
'path' => '/', // or folder id as root
|
||||||
|
'serviceAccountConfigFile' => '', // @String path to config json file
|
||||||
|
));
|
||||||
|
|
||||||
|
// OneDrive volume
|
||||||
|
// Require constant "ELFINDER_ONEDRIVE_CLIENTID" and "ELFINDER_ONEDRIVE_CLIENTSECRET"
|
||||||
|
$opts['roots'][] = array_merge($tmbConfig, array(
|
||||||
|
'phash' => 'g1_Lw', // set parent to Volume group (g1_) root "/" (Lw)
|
||||||
|
'driver' => 'OneDrive',
|
||||||
|
'path' => '/', // or folder id as root
|
||||||
|
'accessToken' => '', // @JSON String access token including refresh token
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
// run elFinder
|
||||||
|
$connector = new elFinderConnector(new elFinder($opts));
|
||||||
|
$connector->run();
|
||||||
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
error_reporting(0); // Set E_ALL for debuging
|
||||||
|
|
||||||
|
// // Optional exec path settings (Default is called with command name only)
|
||||||
|
// define('ELFINDER_TAR_PATH', '/PATH/TO/tar');
|
||||||
|
// define('ELFINDER_GZIP_PATH', '/PATH/TO/gzip');
|
||||||
|
// define('ELFINDER_BZIP2_PATH', '/PATH/TO/bzip2');
|
||||||
|
// define('ELFINDER_XZ_PATH', '/PATH/TO/xz');
|
||||||
|
// define('ELFINDER_ZIP_PATH', '/PATH/TO/zip');
|
||||||
|
// define('ELFINDER_UNZIP_PATH', '/PATH/TO/unzip');
|
||||||
|
// define('ELFINDER_RAR_PATH', '/PATH/TO/rar');
|
||||||
|
// define('ELFINDER_UNRAR_PATH', '/PATH/TO/unrar');
|
||||||
|
// define('ELFINDER_7Z_PATH', '/PATH/TO/7za');
|
||||||
|
// define('ELFINDER_CONVERT_PATH', '/PATH/TO/convert');
|
||||||
|
// define('ELFINDER_IDENTIFY_PATH', '/PATH/TO/identify');
|
||||||
|
// define('ELFINDER_EXIFTRAN_PATH', '/PATH/TO/exiftran');
|
||||||
|
// define('ELFINDER_JPEGTRAN_PATH', '/PATH/TO/jpegtran');
|
||||||
|
// define('ELFINDER_FFMPEG_PATH', '/PATH/TO/ffmpeg');
|
||||||
|
|
||||||
|
// define('ELFINDER_CONNECTOR_URL', 'URL to this connector script'); // see elFinder::getConnectorUrl()
|
||||||
|
|
||||||
|
// define('ELFINDER_DEBUG_ERRORLEVEL', -1); // Error reporting level of debug mode
|
||||||
|
|
||||||
|
// // To Enable(true) handling of PostScript files by ImageMagick
|
||||||
|
// // It is disabled by default as a countermeasure
|
||||||
|
// // of Ghostscript multiple -dSAFER sandbox bypass vulnerabilities
|
||||||
|
// // see https://www.kb.cert.org/vuls/id/332928
|
||||||
|
// define('ELFINDER_IMAGEMAGICK_PS', true);
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // load composer autoload before load elFinder autoload If you need composer
|
||||||
|
// // You need to run the composer command in the php directory.
|
||||||
|
is_readable('./vendor/autoload.php') && require './vendor/autoload.php';
|
||||||
|
|
||||||
|
// // elFinder autoload
|
||||||
|
require './autoload.php';
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Enable FTP connector netmount
|
||||||
|
elFinder::$netDrivers['ftp'] = 'FTP';
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Dropbox network mount
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require kunalvarma05/dropbox-php-sdk` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['dropbox2'] = 'Dropbox2';
|
||||||
|
// // Dropbox2 Netmount driver need next two settings. You can get at https://www.dropbox.com/developers/apps
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=dropbox2&host=1"
|
||||||
|
// // If the elFinder HTML element ID is not "elfinder", you need to change "host=1" to "host=ElementID"
|
||||||
|
// define('ELFINDER_DROPBOX_APPKEY', '');
|
||||||
|
// define('ELFINDER_DROPBOX_APPSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Google Drive network mount
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require google/apiclient:^2.0` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['googledrive'] = 'GoogleDrive';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://console.developers.google.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=googledrive&host=1"
|
||||||
|
// // If the elFinder HTML element ID is not "elfinder", you need to change "host=1" to "host=ElementID"
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTSECRET', '');
|
||||||
|
// // Required case when Google API is NOT added via composer
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT', '/path/to/google-api-php-client/vendor/autoload.php');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Google Drive network mount with Flysystem
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require nao-pon/flysystem-google-drive:~1.1 nao-pon/elfinder-flysystem-driver-ext` on php directory
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['googledrive'] = 'FlysystemGoogleDriveNetmount';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://console.developers.google.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=googledrive&host=1"
|
||||||
|
// // If the elFinder HTML element ID is not "elfinder", you need to change "host=1" to "host=ElementID"
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_GOOGLEDRIVE_CLIENTSECRET', '');
|
||||||
|
// // And "php/.tmp" directory must exist and be writable by PHP.
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for One Drive network mount
|
||||||
|
// // * cURL PHP extension required
|
||||||
|
// // * HTTP server PATH_INFO supports required
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['onedrive'] = 'OneDrive';
|
||||||
|
// // GoogleDrive Netmount driver need next two settings. You can get at https://dev.onedrive.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL/netmount/onedrive/1"
|
||||||
|
// // If the elFinder HTML element ID is not "elfinder", you need to change "/1" to "/ElementID"
|
||||||
|
// define('ELFINDER_ONEDRIVE_CLIENTID', '');
|
||||||
|
// define('ELFINDER_ONEDRIVE_CLIENTSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Required for Box network mount
|
||||||
|
// // * cURL PHP extension required
|
||||||
|
// // Enable network mount
|
||||||
|
// elFinder::$netDrivers['box'] = 'Box';
|
||||||
|
// // Box Netmount driver need next two settings. You can get at https://developer.box.com
|
||||||
|
// // AND require register redirect url to "YOUR_CONNECTOR_URL?cmd=netmount&protocol=box&host=1"
|
||||||
|
// // If the elFinder HTML element ID is not "elfinder", you need to change "host=1" to "host=ElementID"
|
||||||
|
// define('ELFINDER_BOX_CLIENTID', '');
|
||||||
|
// define('ELFINDER_BOX_CLIENTSECRET', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
|
||||||
|
// // Zoho Office Editor APIKey
|
||||||
|
// // https://www.zoho.com/docs/help/office-apis.html
|
||||||
|
// define('ELFINDER_ZOHO_OFFICE_APIKEY', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Online converter (online-convert.com) APIKey
|
||||||
|
// // https://apiv2.online-convert.com/docs/getting_started/api_key.html
|
||||||
|
// define('ELFINDER_ONLINE_CONVERT_APIKEY', '');
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
// // Zip Archive editor
|
||||||
|
// // Installation by composer
|
||||||
|
// // `composer require nao-pon/elfinder-flysystem-ziparchive-netmount` on php directory
|
||||||
|
// define('ELFINDER_DISABLE_ZIPEDITOR', false); // set `true` to disable zip editor
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple function to demonstrate how to control file access using "accessControl" callback.
|
||||||
|
* This method will disable accessing files/folders starting from '.' (dot)
|
||||||
|
*
|
||||||
|
* @param string $attr attribute name (read|write|locked|hidden)
|
||||||
|
* @param string $path absolute file path
|
||||||
|
* @param string $data value of volume option `accessControlData`
|
||||||
|
* @param object $volume elFinder volume driver object
|
||||||
|
* @param bool|null $isDir path is directory (true: directory, false: file, null: unknown)
|
||||||
|
* @param string $relpath file path relative to volume root directory started with directory separator
|
||||||
|
* @return bool|null
|
||||||
|
**/
|
||||||
|
function access($attr, $path, $data, $volume, $isDir, $relpath) {
|
||||||
|
$basename = basename($path);
|
||||||
|
return $basename[0] === '.' // if file/folder begins with '.' (dot)
|
||||||
|
&& strlen($relpath) !== 1 // but with out volume root
|
||||||
|
? !($attr == 'read' || $attr == 'write') // set read+write to false, other (locked+hidden) set to true
|
||||||
|
: null; // else elFinder decide it itself
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Documentation for connector options:
|
||||||
|
// https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options
|
||||||
|
$opts = array(
|
||||||
|
// 'debug' => true,
|
||||||
|
'roots' => array(
|
||||||
|
// Items volume
|
||||||
|
array(
|
||||||
|
'driver' => 'LocalFileSystem', // driver for accessing file system (REQUIRED)
|
||||||
|
'path' => '../files/', // path to files (REQUIRED)
|
||||||
|
'URL' => dirname($_SERVER['PHP_SELF']) . '/../files/', // URL to files (REQUIRED)
|
||||||
|
'trashHash' => 't1_Lw', // elFinder's hash of trash folder
|
||||||
|
'winHashFix' => DIRECTORY_SEPARATOR !== '/', // to make hash same to Linux one on windows too
|
||||||
|
'uploadDeny' => array('all'), // All Mimetypes not allowed to upload
|
||||||
|
'uploadAllow' => array('image/x-ms-bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/x-icon', 'text/plain'), // Mimetype `image` and `text/plain` allowed to upload
|
||||||
|
'uploadOrder' => array('deny', 'allow'), // allowed Mimetype `image` and `text/plain` only
|
||||||
|
'accessControl' => 'access' // disable and hide dot starting files (OPTIONAL)
|
||||||
|
),
|
||||||
|
// Trash volume
|
||||||
|
array(
|
||||||
|
'id' => '1',
|
||||||
|
'driver' => 'Trash',
|
||||||
|
'path' => '../files/.trash/',
|
||||||
|
'tmbURL' => dirname($_SERVER['PHP_SELF']) . '/../files/.trash/.tmb/',
|
||||||
|
'winHashFix' => DIRECTORY_SEPARATOR !== '/', // to make hash same to Linux one on windows too
|
||||||
|
'uploadDeny' => array('all'), // Recomend the same settings as the original volume that uses the trash
|
||||||
|
'uploadAllow' => array('image/x-ms-bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/x-icon', 'text/plain'), // Same as above
|
||||||
|
'uploadOrder' => array('deny', 'allow'), // Same as above
|
||||||
|
'accessControl' => 'access', // Same as above
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// run elFinder
|
||||||
|
$connector = new elFinderConnector(new elFinder($opts));
|
||||||
|
$connector->run();
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default elFinder connector
|
||||||
|
*
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
**/
|
||||||
|
class elFinderConnector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* elFinder instance
|
||||||
|
*
|
||||||
|
* @var elFinder
|
||||||
|
**/
|
||||||
|
protected $elFinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
**/
|
||||||
|
protected $options = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be use output($data) $data['header']
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
* @deprecated
|
||||||
|
**/
|
||||||
|
protected $header = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP request method
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $reqMethod = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content type of output JSON
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $contentType = 'Content-Type: application/json; charset=utf-8';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param $elFinder
|
||||||
|
* @param bool $debug
|
||||||
|
*
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
*/
|
||||||
|
public function __construct($elFinder, $debug = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->elFinder = $elFinder;
|
||||||
|
$this->reqMethod = strtoupper($_SERVER["REQUEST_METHOD"]);
|
||||||
|
if ($debug) {
|
||||||
|
self::$contentType = 'Content-Type: text/plain; charset=utf-8';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute elFinder command and output result
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$isPost = $this->reqMethod === 'POST';
|
||||||
|
$src = $isPost ? array_merge($_GET, $_POST) : $_GET;
|
||||||
|
$maxInputVars = (!$src || isset($src['targets'])) ? ini_get('max_input_vars') : null;
|
||||||
|
if ((!$src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) {
|
||||||
|
// for max_input_vars and supports IE XDomainRequest()
|
||||||
|
$parts = explode('&', $rawPostData);
|
||||||
|
if (!$src || $maxInputVars < count($parts)) {
|
||||||
|
$src = array();
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
list($key, $value) = array_pad(explode('=', $part), 2, '');
|
||||||
|
$key = rawurldecode($key);
|
||||||
|
if (preg_match('/^(.+?)\[([^\[\]]*)\]$/', $key, $m)) {
|
||||||
|
$key = $m[1];
|
||||||
|
$idx = $m[2];
|
||||||
|
if (!isset($src[$key])) {
|
||||||
|
$src[$key] = array();
|
||||||
|
}
|
||||||
|
if ($idx) {
|
||||||
|
$src[$key][$idx] = rawurldecode($value);
|
||||||
|
} else {
|
||||||
|
$src[$key][] = rawurldecode($value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$src[$key] = rawurldecode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_POST = $this->input_filter($src);
|
||||||
|
$_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($src['targets']) && $this->elFinder->maxTargets && count($src['targets']) > $this->elFinder->maxTargets) {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_MAX_TARGTES)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = isset($src['cmd']) ? $src['cmd'] : '';
|
||||||
|
$args = array();
|
||||||
|
|
||||||
|
if (!function_exists('json_encode')) {
|
||||||
|
$error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON);
|
||||||
|
$this->output(array('error' => '{"error":["' . implode('","', $error) . '"]}', 'raw' => true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->elFinder->loaded()) {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors));
|
||||||
|
}
|
||||||
|
|
||||||
|
// telepat_mode: on
|
||||||
|
if (!$cmd && $isPost) {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html'));
|
||||||
|
}
|
||||||
|
// telepat_mode: off
|
||||||
|
|
||||||
|
if (!$this->elFinder->commandExists($cmd)) {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect required arguments to exec command
|
||||||
|
$hasFiles = false;
|
||||||
|
foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) {
|
||||||
|
if ($name === 'FILES') {
|
||||||
|
if (isset($_FILES)) {
|
||||||
|
$hasFiles = true;
|
||||||
|
} elseif ($req) {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$arg = isset($src[$name]) ? $src[$name] : '';
|
||||||
|
|
||||||
|
if (!is_array($arg) && $req !== '') {
|
||||||
|
$arg = trim($arg);
|
||||||
|
}
|
||||||
|
if ($req && $arg === '') {
|
||||||
|
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)));
|
||||||
|
}
|
||||||
|
$args[$name] = $arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$args['debug'] = isset($src['debug']) ? !!$src['debug'] : false;
|
||||||
|
|
||||||
|
$args = $this->input_filter($args);
|
||||||
|
if ($hasFiles) {
|
||||||
|
$args['FILES'] = $_FILES;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->output($this->elFinder->exec($cmd, $args));
|
||||||
|
} catch (elFinderAbortException $e) {
|
||||||
|
// connection aborted
|
||||||
|
// unlock session data for multiple access
|
||||||
|
$this->elFinder->getSession()->close();
|
||||||
|
// HTTP response code
|
||||||
|
header('HTTP/1.0 204 No Content');
|
||||||
|
// clear output buffer
|
||||||
|
while (ob_get_level() && ob_end_clean()) {
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the header.
|
||||||
|
*
|
||||||
|
* @param array|string $value HTTP header(s)
|
||||||
|
*/
|
||||||
|
public function setHeader($value)
|
||||||
|
{
|
||||||
|
$this->header = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output json
|
||||||
|
*
|
||||||
|
* @param array data to output
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws elFinderAbortException
|
||||||
|
* @author Dmitry (dio) Levashov
|
||||||
|
*/
|
||||||
|
protected function output(array $data)
|
||||||
|
{
|
||||||
|
// unlock session data for multiple access
|
||||||
|
$this->elFinder->getSession()->close();
|
||||||
|
// client disconnect should abort
|
||||||
|
ignore_user_abort(false);
|
||||||
|
|
||||||
|
if ($this->header) {
|
||||||
|
self::sendHeader($this->header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['pointer'])) {
|
||||||
|
// set time limit to 0
|
||||||
|
elFinder::extendTimeLimit(0);
|
||||||
|
|
||||||
|
// send optional header
|
||||||
|
if (!empty($data['header'])) {
|
||||||
|
self::sendHeader($data['header']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear output buffer
|
||||||
|
while (ob_get_level() && ob_end_clean()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
$toEnd = true;
|
||||||
|
$fp = $data['pointer'];
|
||||||
|
$sendData = !($this->reqMethod === 'HEAD' || !empty($data['info']['xsendfile']));
|
||||||
|
$psize = null;
|
||||||
|
if (($this->reqMethod === 'GET' || !$sendData)
|
||||||
|
&& (elFinder::isSeekableStream($fp) || elFinder::isSeekableUrl($fp))
|
||||||
|
&& (array_search('Accept-Ranges: none', headers_list()) === false)) {
|
||||||
|
header('Accept-Ranges: bytes');
|
||||||
|
if (!empty($_SERVER['HTTP_RANGE'])) {
|
||||||
|
$size = $data['info']['size'];
|
||||||
|
$end = $size - 1;
|
||||||
|
if (preg_match('/bytes=(\d*)-(\d*)(,?)/i', $_SERVER['HTTP_RANGE'], $matches)) {
|
||||||
|
if (empty($matches[3])) {
|
||||||
|
if (empty($matches[1]) && $matches[1] !== '0') {
|
||||||
|
$start = $size - $matches[2];
|
||||||
|
} else {
|
||||||
|
$start = intval($matches[1]);
|
||||||
|
if (!empty($matches[2])) {
|
||||||
|
$end = intval($matches[2]);
|
||||||
|
if ($end >= $size) {
|
||||||
|
$end = $size - 1;
|
||||||
|
}
|
||||||
|
$toEnd = ($end == ($size - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$psize = $end - $start + 1;
|
||||||
|
|
||||||
|
header('HTTP/1.1 206 Partial Content');
|
||||||
|
header('Content-Length: ' . $psize);
|
||||||
|
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
|
||||||
|
|
||||||
|
// Apache mod_xsendfile dose not support range request
|
||||||
|
if (isset($data['info']['xsendfile']) && strtolower($data['info']['xsendfile']) === 'x-sendfile') {
|
||||||
|
if (function_exists('header_remove')) {
|
||||||
|
header_remove($data['info']['xsendfile']);
|
||||||
|
} else {
|
||||||
|
header($data['info']['xsendfile'] . ':');
|
||||||
|
}
|
||||||
|
unset($data['info']['xsendfile']);
|
||||||
|
if ($this->reqMethod !== 'HEAD') {
|
||||||
|
$sendData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sendData && !elFinder::isSeekableUrl($fp) && fseek($fp, $start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($sendData && is_null($psize)) {
|
||||||
|
elFinder::rewind($fp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header('Accept-Ranges: none');
|
||||||
|
if (isset($data['info']) && !$data['info']['size']) {
|
||||||
|
if (function_exists('header_remove')) {
|
||||||
|
header_remove('Content-Length');
|
||||||
|
} else {
|
||||||
|
header('Content-Length:');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sendData) {
|
||||||
|
if ($toEnd || elFinder::isSeekableUrl($fp)) {
|
||||||
|
// PHP < 5.6 has a bug of fpassthru
|
||||||
|
// see https://bugs.php.net/bug.php?id=66736
|
||||||
|
if (version_compare(PHP_VERSION, '5.6', '<')) {
|
||||||
|
file_put_contents('php://output', $fp);
|
||||||
|
} else {
|
||||||
|
fpassthru($fp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$out = fopen('php://output', 'wb');
|
||||||
|
stream_copy_to_stream($fp, $out, $psize);
|
||||||
|
fclose($out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($data['volume'])) {
|
||||||
|
$data['volume']->close($fp, $data['info']['hash']);
|
||||||
|
} else {
|
||||||
|
fclose($fp);
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
self::outputJson($data);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove null & stripslashes applies on "magic_quotes_gpc"
|
||||||
|
*
|
||||||
|
* @param mixed $args
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @author Naoki Sawada
|
||||||
|
*/
|
||||||
|
protected function input_filter($args)
|
||||||
|
{
|
||||||
|
static $magic_quotes_gpc = NULL;
|
||||||
|
|
||||||
|
if ($magic_quotes_gpc === NULL)
|
||||||
|
$magic_quotes_gpc = (version_compare(PHP_VERSION, '5.4', '<') && get_magic_quotes_gpc());
|
||||||
|
|
||||||
|
if (is_array($args)) {
|
||||||
|
return array_map(array(& $this, 'input_filter'), $args);
|
||||||
|
}
|
||||||
|
$res = str_replace("\0", '', $args);
|
||||||
|
$magic_quotes_gpc && ($res = stripslashes($res));
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send HTTP header
|
||||||
|
*
|
||||||
|
* @param string|array $header optional header
|
||||||
|
*/
|
||||||
|
protected static function sendHeader($header = null)
|
||||||
|
{
|
||||||
|
if ($header) {
|
||||||
|
if (is_array($header)) {
|
||||||
|
foreach ($header as $h) {
|
||||||
|
header($h);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header($header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output JSON
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
*/
|
||||||
|
public static function outputJson($data)
|
||||||
|
{
|
||||||
|
// send header
|
||||||
|
$header = isset($data['header']) ? $data['header'] : self::$contentType;
|
||||||
|
self::sendHeader($header);
|
||||||
|
|
||||||
|
unset($data['header']);
|
||||||
|
|
||||||
|
if (!empty($data['raw']) && isset($data['error'])) {
|
||||||
|
$out = $data['error'];
|
||||||
|
} else {
|
||||||
|
if (isset($data['debug']) && isset($data['debug']['backendErrors'])) {
|
||||||
|
$data['debug']['backendErrors'] = array_merge($data['debug']['backendErrors'], elFinder::$phpErrors);
|
||||||
|
}
|
||||||
|
$out = json_encode($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear output buffer
|
||||||
|
while (ob_get_level() && ob_end_clean()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Length: ' . strlen($out));
|
||||||
|
|
||||||
|
echo $out;
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}// END class
|
||||||
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use League\Flysystem\Filesystem;
|
||||||
|
use League\Flysystem\Adapter\Local;
|
||||||
|
use League\Flysystem\Cached\CachedAdapter;
|
||||||
|
use League\Flysystem\Cached\Storage\Adapter as ACache;
|
||||||
|
use Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter;
|
||||||
|
use Hypweb\Flysystem\Cached\Extra\Hasdir;
|
||||||
|
use Hypweb\Flysystem\Cached\Extra\DisableEnsureParentDirectories;
|
||||||
|
use Hypweb\elFinderFlysystemDriverExt\Driver as ExtDriver;
|
||||||
|
|
||||||
|
elFinder::$netDrivers['googledrive'] = 'FlysystemGoogleDriveNetmount';
|
||||||
|
|
||||||
|
if (!class_exists('elFinderVolumeFlysystemGoogleDriveCache', false)) {
|
||||||
|
class elFinderVolumeFlysystemGoogleDriveCache extends ACache
|
||||||
|
{
|
||||||
|
use Hasdir;
|
||||||
|
use DisableEnsureParentDirectories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$opts = array(
|
||||||
|
'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
|
||||||
|
'rootCssClass' => 'elfinder-navbar-root-googledrive',
|
||||||
|
'gdAlias' => '%s@GDrive',
|
||||||
|
'gdCacheDir' => __DIR__ . '/.tmp',
|
||||||
|
'gdCachePrefix' => 'gd-',
|
||||||
|
'gdCacheExpire' => 600
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->options = array_merge($this->options, $opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare driver before mount volume.
|
||||||
|
* Return true if volume is ready.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
**/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
if (empty($this->options['icon'])) {
|
||||||
|
$this->options['icon'] = true;
|
||||||
|
}
|
||||||
|
if ($res = parent::init()) {
|
||||||
|
if ($this->options['icon'] === true) {
|
||||||
|
unset($this->options['icon']);
|
||||||
|
}
|
||||||
|
// enable command archive
|
||||||
|
$this->options['useRemoteArchive'] = true;
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare
|
||||||
|
* Call from elFinder::netmout() before volume->mount()
|
||||||
|
*
|
||||||
|
* @param $options
|
||||||
|
*
|
||||||
|
* @return Array
|
||||||
|
* @author Naoki Sawada
|
||||||
|
*/
|
||||||
|
public function netmountPrepare($options)
|
||||||
|
{
|
||||||
|
if (empty($options['client_id']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTID')) {
|
||||||
|
$options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
|
||||||
|
}
|
||||||
|
if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) {
|
||||||
|
$options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($options['pass'])) {
|
||||||
|
$options['pass'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client = new \Google_Client();
|
||||||
|
$client->setClientId($options['client_id']);
|
||||||
|
$client->setClientSecret($options['client_secret']);
|
||||||
|
|
||||||
|
if ($options['pass'] === 'reauth') {
|
||||||
|
$options['pass'] = '';
|
||||||
|
$this->session->set('GoogleDriveAuthParams', [])->set('GoogleDriveTokens', []);
|
||||||
|
} else if ($options['pass'] === 'googledrive') {
|
||||||
|
$options['pass'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options);
|
||||||
|
|
||||||
|
if (!isset($options['access_token'])) {
|
||||||
|
$options['access_token'] = $this->session->get('GoogleDriveTokens', []);
|
||||||
|
$this->session->remove('GoogleDriveTokens');
|
||||||
|
}
|
||||||
|
$aToken = $options['access_token'];
|
||||||
|
|
||||||
|
$rootObj = $service = null;
|
||||||
|
if ($aToken) {
|
||||||
|
try {
|
||||||
|
$client->setAccessToken($aToken);
|
||||||
|
if ($client->isAccessTokenExpired()) {
|
||||||
|
$aToken = array_merge($aToken, $client->fetchAccessTokenWithRefreshToken());
|
||||||
|
$client->setAccessToken($aToken);
|
||||||
|
}
|
||||||
|
$service = new \Google_Service_Drive($client);
|
||||||
|
$rootObj = $service->files->get('root');
|
||||||
|
|
||||||
|
$options['access_token'] = $aToken;
|
||||||
|
$this->session->set('GoogleDriveAuthParams', $options);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$aToken = [];
|
||||||
|
$options['access_token'] = [];
|
||||||
|
if ($options['user'] !== 'init') {
|
||||||
|
$this->session->set('GoogleDriveAuthParams', $options);
|
||||||
|
return array('exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$itpCare = isset($options['code']);
|
||||||
|
$code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
|
||||||
|
if ($code || $options['user'] === 'init') {
|
||||||
|
if (empty($options['url'])) {
|
||||||
|
$options['url'] = elFinder::getConnectorUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($options['id'])) {
|
||||||
|
$callback = $options['url'] . (strpos($options['url'], '?') !== false? '&' : '?') . 'cmd=netmount&protocol=googledrive&host=' . ($options['id'] === 'elfinder'? '1' : $options['id']);
|
||||||
|
$client->setRedirectUri($callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$aToken && empty($code)) {
|
||||||
|
$client->setScopes([Google_Service_Drive::DRIVE]);
|
||||||
|
if (!empty($options['offline'])) {
|
||||||
|
$client->setApprovalPrompt('force');
|
||||||
|
$client->setAccessType('offline');
|
||||||
|
}
|
||||||
|
$url = $client->createAuthUrl();
|
||||||
|
|
||||||
|
$html = '<input id="elf-volumedriver-googledrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
|
||||||
|
$html .= '<script>
|
||||||
|
$("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "googledrive", mode: "makebtn", url: "' . $url . '"});
|
||||||
|
</script>';
|
||||||
|
if (empty($options['pass']) && $options['host'] !== '1') {
|
||||||
|
$options['pass'] = 'return';
|
||||||
|
$this->session->set('GoogleDriveAuthParams', $options);
|
||||||
|
return array('exit' => true, 'body' => $html);
|
||||||
|
} else {
|
||||||
|
$out = array(
|
||||||
|
'node' => $options['id'],
|
||||||
|
'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}',
|
||||||
|
'bind' => 'netmount'
|
||||||
|
);
|
||||||
|
return array('exit' => 'callback', 'out' => $out);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($code) {
|
||||||
|
if (!empty($options['id'])) {
|
||||||
|
$aToken = $client->fetchAccessTokenWithAuthCode($code);
|
||||||
|
$options['access_token'] = $aToken;
|
||||||
|
unset($options['code']);
|
||||||
|
$this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options);
|
||||||
|
$out = array(
|
||||||
|
'node' => $options['id'],
|
||||||
|
'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}',
|
||||||
|
'bind' => 'netmount'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
|
||||||
|
$out = array(
|
||||||
|
'node' => $nodeid,
|
||||||
|
'json' => json_encode(array(
|
||||||
|
'protocol' => 'googledrive',
|
||||||
|
'host' => $nodeid,
|
||||||
|
'mode' => 'redirect',
|
||||||
|
'options' => array(
|
||||||
|
'id' => $nodeid,
|
||||||
|
'code'=> $code
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
'bind' => 'netmount'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!$itpCare) {
|
||||||
|
return array('exit' => 'callback', 'out' => $out);
|
||||||
|
} else {
|
||||||
|
return array('exit' => true, 'body' => $out['json']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$folders = [];
|
||||||
|
foreach ($service->files->listFiles([
|
||||||
|
'pageSize' => 1000,
|
||||||
|
'q' => 'trashed = false and mimeType = "application/vnd.google-apps.folder"'
|
||||||
|
]) as $f) {
|
||||||
|
$folders[$f->getId()] = $f->getName();
|
||||||
|
}
|
||||||
|
natcasesort($folders);
|
||||||
|
$folders = ['root' => $rootObj->getName()] + $folders;
|
||||||
|
$folders = json_encode($folders);
|
||||||
|
$json = '{"protocol": "googledrive", "mode": "done", "folders": ' . $folders . '}';
|
||||||
|
$options['pass'] = 'return';
|
||||||
|
$html = 'Google.com';
|
||||||
|
$html .= '<script>
|
||||||
|
$("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
|
||||||
|
</script>';
|
||||||
|
$this->session->set('GoogleDriveAuthParams', $options);
|
||||||
|
return array('exit' => true, 'body' => $html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens');
|
||||||
|
if (empty($options['pass'])) {
|
||||||
|
return array('exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage());
|
||||||
|
} else {
|
||||||
|
return array('exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$aToken) {
|
||||||
|
return array('exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($options['path'] === '/') {
|
||||||
|
$options['path'] = 'root';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file = $service->files->get($options['path']);
|
||||||
|
$options['alias'] = sprintf($this->options['gdAlias'], $file->getName());
|
||||||
|
if (!empty($this->options['netkey'])) {
|
||||||
|
elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
|
||||||
|
}
|
||||||
|
} catch (Google_Service_Exception $e) {
|
||||||
|
$err = json_decode($e->getMessage(), true);
|
||||||
|
if (isset($err['error']) && $err['error']['code'] == 404) {
|
||||||
|
return array('exit' => true, 'error' => [elFinder::ERROR_TRGDIR_NOT_FOUND, $options['path']]);
|
||||||
|
} else {
|
||||||
|
return array('exit' => true, 'error' => $e->getMessage());
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return array('exit' => true, 'error' => $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) {
|
||||||
|
unset($options[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* process of on netunmount
|
||||||
|
* Drop table `dropbox` & rm thumbs
|
||||||
|
*
|
||||||
|
* @param $netVolumes
|
||||||
|
* @param $key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @internal param array $options
|
||||||
|
*/
|
||||||
|
public function netunmount($netVolumes, $key)
|
||||||
|
{
|
||||||
|
$cache = $this->options['gdCacheDir'] . DIRECTORY_SEPARATOR . $this->options['gdCachePrefix'] . $this->netMountKey;
|
||||||
|
if (file_exists($cache) && is_writeable($cache)) {
|
||||||
|
unlink($cache);
|
||||||
|
}
|
||||||
|
if ($tmbs = glob($this->tmbPath . DIRECTORY_SEPARATOR . $this->netMountKey . '*')) {
|
||||||
|
foreach ($tmbs as $file) {
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Mount" volume.
|
||||||
|
* Return true if volume available for read or write,
|
||||||
|
* false - otherwise
|
||||||
|
*
|
||||||
|
* @param array $opts
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @author Naoki Sawada
|
||||||
|
*/
|
||||||
|
public function mount(array $opts)
|
||||||
|
{
|
||||||
|
$creds = null;
|
||||||
|
if (isset($opts['access_token'])) {
|
||||||
|
$this->netMountKey = md5(join('-', array('googledrive', $opts['path'], (isset($opts['access_token']['refresh_token']) ? $opts['access_token']['refresh_token'] : $opts['access_token']['access_token']))));
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = new \Google_Client();
|
||||||
|
$client->setClientId($opts['client_id']);
|
||||||
|
$client->setClientSecret($opts['client_secret']);
|
||||||
|
|
||||||
|
if (!empty($opts['access_token'])) {
|
||||||
|
$client->setAccessToken($opts['access_token']);
|
||||||
|
}
|
||||||
|
if ($this->needOnline && $client->isAccessTokenExpired()) {
|
||||||
|
try {
|
||||||
|
$creds = $client->fetchAccessTokenWithRefreshToken();
|
||||||
|
} catch (LogicException $e) {
|
||||||
|
$this->session->remove('GoogleDriveAuthParams');
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$service = new \Google_Service_Drive($client);
|
||||||
|
|
||||||
|
// If path is not set, use the root
|
||||||
|
if (!isset($opts['path']) || $opts['path'] === '') {
|
||||||
|
$opts['path'] = 'root';
|
||||||
|
}
|
||||||
|
|
||||||
|
$googleDrive = new GoogleDriveAdapter($service, $opts['path'], ['useHasDir' => true]);
|
||||||
|
|
||||||
|
$opts['fscache'] = null;
|
||||||
|
if ($this->options['gdCacheDir'] && is_writeable($this->options['gdCacheDir'])) {
|
||||||
|
if ($this->options['gdCacheExpire']) {
|
||||||
|
$opts['fscache'] = new elFinderVolumeFlysystemGoogleDriveCache(new Local($this->options['gdCacheDir']), $this->options['gdCachePrefix'] . $this->netMountKey, $this->options['gdCacheExpire']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($opts['fscache']) {
|
||||||
|
$filesystem = new Filesystem(new CachedAdapter($googleDrive, $opts['fscache']));
|
||||||
|
} else {
|
||||||
|
$filesystem = new Filesystem($googleDrive);
|
||||||
|
}
|
||||||
|
|
||||||
|
$opts['driver'] = 'FlysystemExt';
|
||||||
|
$opts['filesystem'] = $filesystem;
|
||||||
|
$opts['separator'] = '/';
|
||||||
|
$opts['checkSubfolders'] = true;
|
||||||
|
if (!isset($opts['alias'])) {
|
||||||
|
$opts['alias'] = 'GoogleDrive';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($res = parent::mount($opts)) {
|
||||||
|
// update access_token of session data
|
||||||
|
if ($creds) {
|
||||||
|
$netVolumes = $this->session->get('netvolume');
|
||||||
|
$netVolumes[$this->netMountKey]['access_token'] = array_merge($netVolumes[$this->netMountKey]['access_token'], $creds);
|
||||||
|
$this->session->set('netvolume', $netVolumes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected function tmbname($stat)
|
||||||
|
{
|
||||||
|
return $this->netMountKey . substr(substr($stat['hash'], strlen($this->id)), -38) . $stat['ts'] . '.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return debug info for client.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
**/
|
||||||
|
public function debug()
|
||||||
|
{
|
||||||
|
$res = parent::debug();
|
||||||
|
if (!empty($this->options['netkey']) && empty($this->options['refresh_token']) && $this->options['access_token'] && isset($this->options['access_token']['refresh_token'])) {
|
||||||
|
$res['refresh_token'] = $this->options['access_token']['refresh_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder Plugin Abstract
|
||||||
|
*
|
||||||
|
* @package elfinder
|
||||||
|
* @author Naoki Sawada
|
||||||
|
* @license New BSD
|
||||||
|
*/
|
||||||
|
class elFinderPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This plugin's options
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $opts = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current volume's options
|
||||||
|
*
|
||||||
|
* @param object $volume
|
||||||
|
*
|
||||||
|
* @return array options
|
||||||
|
*/
|
||||||
|
protected function getCurrentOpts($volume)
|
||||||
|
{
|
||||||
|
$name = substr(get_class($this), 14); // remove "elFinderPlugin"
|
||||||
|
$opts = $this->opts;
|
||||||
|
if (is_object($volume)) {
|
||||||
|
$volOpts = $volume->getOptionsPlugin($name);
|
||||||
|
if (is_array($volOpts)) {
|
||||||
|
$opts = array_merge($opts, $volOpts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is enabled with options
|
||||||
|
*
|
||||||
|
* @param array $opts
|
||||||
|
* @param elFinder $elfinder
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function iaEnabled($opts, $elfinder = null)
|
||||||
|
{
|
||||||
|
if (!$opts['enable']) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check post var 'contentSaveId' to disable this plugin
|
||||||
|
if ($elfinder && !empty($opts['disableWithContentSaveId'])) {
|
||||||
|
$session = $elfinder->getSession();
|
||||||
|
$urlContentSaveIds = $session->get('urlContentSaveIds', array());
|
||||||
|
if (!empty(elFinder::$currentArgs['contentSaveId']) && ($contentSaveId = elFinder::$currentArgs['contentSaveId'])) {
|
||||||
|
if (!empty($urlContentSaveIds[$contentSaveId])) {
|
||||||
|
$elfinder->removeUrlContentSaveId($contentSaveId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($opts['onDropWith']) && !is_null($opts['onDropWith'])) {
|
||||||
|
// plugin disabled by default, enabled only if given key is pressed
|
||||||
|
if (isset($_REQUEST['dropWith']) && $_REQUEST['dropWith']) {
|
||||||
|
$onDropWith = $opts['onDropWith'];
|
||||||
|
$action = (int)$_REQUEST['dropWith'];
|
||||||
|
if (!is_array($onDropWith)) {
|
||||||
|
$onDropWith = array($onDropWith);
|
||||||
|
}
|
||||||
|
foreach ($onDropWith as $key) {
|
||||||
|
$key = (int)$key;
|
||||||
|
if (($action & $key) === $key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($opts['offDropWith']) && !is_null($opts['offDropWith']) && isset($_REQUEST['dropWith'])) {
|
||||||
|
// plugin enabled by default, disabled only if given key is pressed
|
||||||
|
$offDropWith = $opts['offDropWith'];
|
||||||
|
$action = (int)$_REQUEST['dropWith'];
|
||||||
|
if (!is_array($offDropWith)) {
|
||||||
|
$offDropWith = array($offDropWith);
|
||||||
|
}
|
||||||
|
$res = true;
|
||||||
|
foreach ($offDropWith as $key) {
|
||||||
|
$key = (int)$key;
|
||||||
|
if ($key === 0) {
|
||||||
|
if ($action === 0) {
|
||||||
|
$res = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (($action & $key) === $key) {
|
||||||
|
$res = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$res) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,337 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder - file manager for web.
|
||||||
|
* Session Wrapper Class.
|
||||||
|
*
|
||||||
|
* @package elfinder
|
||||||
|
* @author Naoki Sawada
|
||||||
|
**/
|
||||||
|
|
||||||
|
class elFinderSession implements elFinderSessionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A flag of session started
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $started = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To fix PHP bug that duplicate Set-Cookie header to be sent
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
* @see https://bugs.php.net/bug.php?id=75554
|
||||||
|
*/
|
||||||
|
protected $fixCookieRegist = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of session keys of this instance
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $keys = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is enabled base64encode
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $base64encode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options array
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $opts = array(
|
||||||
|
'base64encode' => false,
|
||||||
|
'keys' => array(
|
||||||
|
'default' => 'elFinderCaches',
|
||||||
|
'netvolume' => 'elFinderNetVolumes'
|
||||||
|
),
|
||||||
|
'cookieParams' => array()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constractor
|
||||||
|
*
|
||||||
|
* @param array $opts The options
|
||||||
|
*
|
||||||
|
* @return self Instanse of this class
|
||||||
|
*/
|
||||||
|
public function __construct($opts)
|
||||||
|
{
|
||||||
|
$this->opts = array_merge($this->opts, $opts);
|
||||||
|
$this->base64encode = !empty($this->opts['base64encode']);
|
||||||
|
$this->keys = $this->opts['keys'];
|
||||||
|
if (function_exists('apache_get_version') || $this->opts['cookieParams']) {
|
||||||
|
$this->fixCookieRegist = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function get($key, $empty = null)
|
||||||
|
{
|
||||||
|
$closed = false;
|
||||||
|
if (!$this->started) {
|
||||||
|
$closed = true;
|
||||||
|
$this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = null;
|
||||||
|
|
||||||
|
if ($this->started) {
|
||||||
|
$session =& $this->getSessionRef($key);
|
||||||
|
$data = $session;
|
||||||
|
if ($data && $this->base64encode) {
|
||||||
|
$data = $this->decodeData($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkFn = null;
|
||||||
|
if (!is_null($empty)) {
|
||||||
|
if (is_string($empty)) {
|
||||||
|
$checkFn = 'is_string';
|
||||||
|
} elseif (is_array($empty)) {
|
||||||
|
$checkFn = 'is_array';
|
||||||
|
} elseif (is_object($empty)) {
|
||||||
|
$checkFn = 'is_object';
|
||||||
|
} elseif (is_float($empty)) {
|
||||||
|
$checkFn = 'is_float';
|
||||||
|
} elseif (is_int($empty)) {
|
||||||
|
$checkFn = 'is_int';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($data) || ($checkFn && !$checkFn($data))) {
|
||||||
|
$session = $data = $empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($closed) {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function start()
|
||||||
|
{
|
||||||
|
set_error_handler(array($this, 'session_start_error'), E_NOTICE | E_WARNING);
|
||||||
|
|
||||||
|
// apache2 SAPI has a bug of session cookie register
|
||||||
|
// see https://bugs.php.net/bug.php?id=75554
|
||||||
|
// see https://github.com/php/php-src/pull/3231
|
||||||
|
if ($this->fixCookieRegist === true) {
|
||||||
|
if ((int)ini_get('session.use_cookies') === 1) {
|
||||||
|
if (ini_set('session.use_cookies', 0) === false) {
|
||||||
|
$this->fixCookieRegist === false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
|
||||||
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
$this->started = session_id() ? true : false;
|
||||||
|
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get variable reference of $_SESSION
|
||||||
|
*
|
||||||
|
* @param string $key key of $_SESSION array
|
||||||
|
*
|
||||||
|
* @return mixed|null
|
||||||
|
*/
|
||||||
|
protected function & getSessionRef($key)
|
||||||
|
{
|
||||||
|
$session = null;
|
||||||
|
if ($this->started) {
|
||||||
|
list($cat, $name) = array_pad(explode('.', $key, 2), 2, null);
|
||||||
|
if (is_null($name)) {
|
||||||
|
if (!isset($this->keys[$cat])) {
|
||||||
|
$name = $cat;
|
||||||
|
$cat = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($this->keys[$cat])) {
|
||||||
|
$cat = $this->keys[$cat];
|
||||||
|
} else {
|
||||||
|
$name = $cat . '.' . $name;
|
||||||
|
$cat = $this->keys['default'];
|
||||||
|
}
|
||||||
|
if (is_null($name)) {
|
||||||
|
if (!isset($_SESSION[$cat])) {
|
||||||
|
$_SESSION[$cat] = null;
|
||||||
|
}
|
||||||
|
$session =& $_SESSION[$cat];
|
||||||
|
} else {
|
||||||
|
if (!isset($_SESSION[$cat]) || !is_array($_SESSION[$cat])) {
|
||||||
|
$_SESSION[$cat] = array();
|
||||||
|
}
|
||||||
|
if (!isset($_SESSION[$cat][$name])) {
|
||||||
|
$_SESSION[$cat][$name] = null;
|
||||||
|
}
|
||||||
|
$session =& $_SESSION[$cat][$name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64 decode of session val
|
||||||
|
*
|
||||||
|
* @param $data
|
||||||
|
*
|
||||||
|
* @return bool|mixed|string|null
|
||||||
|
*/
|
||||||
|
protected function decodeData($data)
|
||||||
|
{
|
||||||
|
if ($this->base64encode) {
|
||||||
|
if (is_string($data)) {
|
||||||
|
if (($data = base64_decode($data)) !== false) {
|
||||||
|
$data = unserialize($data);
|
||||||
|
} else {
|
||||||
|
$data = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$data = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
if ($this->started) {
|
||||||
|
if ($this->fixCookieRegist === true) {
|
||||||
|
// regist cookie only once for apache2 SAPI
|
||||||
|
$cParm = session_get_cookie_params();
|
||||||
|
if ($this->opts['cookieParams'] && is_array($this->opts['cookieParams'])) {
|
||||||
|
$cParm = array_merge($cParm, $this->opts['cookieParams']);
|
||||||
|
}
|
||||||
|
if (version_compare(PHP_VERSION, '7.3', '<')) {
|
||||||
|
setcookie(session_name(), session_id(), 0, $cParm['path'] . (!empty($cParm['SameSite'])? '; SameSite=' . $cParm['SameSite'] : ''), $cParm['domain'], $cParm['secure'], $cParm['httponly']);
|
||||||
|
} else {
|
||||||
|
$allows = array('expires' => true, 'path' => true, 'domain' => true, 'secure' => true, 'httponly' => true, 'samesite' => true);
|
||||||
|
foreach(array_keys($cParm) as $_k) {
|
||||||
|
if (!isset($allows[$_k])) {
|
||||||
|
unset($cParm[$_k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setcookie(session_name(), session_id(), $cParm);
|
||||||
|
}
|
||||||
|
$this->fixCookieRegist = false;
|
||||||
|
}
|
||||||
|
session_write_close();
|
||||||
|
}
|
||||||
|
$this->started = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function set($key, $data)
|
||||||
|
{
|
||||||
|
$closed = false;
|
||||||
|
if (!$this->started) {
|
||||||
|
$closed = true;
|
||||||
|
$this->start();
|
||||||
|
}
|
||||||
|
$session =& $this->getSessionRef($key);
|
||||||
|
if ($this->base64encode) {
|
||||||
|
$data = $this->encodeData($data);
|
||||||
|
}
|
||||||
|
$session = $data;
|
||||||
|
|
||||||
|
if ($closed) {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64 encode for session val
|
||||||
|
*
|
||||||
|
* @param $data
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function encodeData($data)
|
||||||
|
{
|
||||||
|
if ($this->base64encode) {
|
||||||
|
$data = base64_encode(serialize($data));
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function remove($key)
|
||||||
|
{
|
||||||
|
$closed = false;
|
||||||
|
if (!$this->started) {
|
||||||
|
$closed = true;
|
||||||
|
$this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
list($cat, $name) = array_pad(explode('.', $key, 2), 2, null);
|
||||||
|
if (is_null($name)) {
|
||||||
|
if (!isset($this->keys[$cat])) {
|
||||||
|
$name = $cat;
|
||||||
|
$cat = 'default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($this->keys[$cat])) {
|
||||||
|
$cat = $this->keys[$cat];
|
||||||
|
} else {
|
||||||
|
$name = $cat . '.' . $name;
|
||||||
|
$cat = $this->keys['default'];
|
||||||
|
}
|
||||||
|
if (is_null($name)) {
|
||||||
|
unset($_SESSION[$cat]);
|
||||||
|
} else {
|
||||||
|
if (isset($_SESSION[$cat]) && is_array($_SESSION[$cat])) {
|
||||||
|
unset($_SESSION[$cat][$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($closed) {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sessioin error handler (Only for suppression of error at session start)
|
||||||
|
*
|
||||||
|
* @param $errno
|
||||||
|
* @param $errstr
|
||||||
|
*/
|
||||||
|
protected function session_start_error($errno, $errstr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder - file manager for web.
|
||||||
|
* Session Wrapper Interface.
|
||||||
|
*
|
||||||
|
* @package elfinder
|
||||||
|
* @author Naoki Sawada
|
||||||
|
**/
|
||||||
|
|
||||||
|
interface elFinderSessionInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Session start
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
**/
|
||||||
|
public function start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session write & close
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
**/
|
||||||
|
public function close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session data
|
||||||
|
* This method must be equipped with an automatic start / close.
|
||||||
|
*
|
||||||
|
* @param string $key Target key
|
||||||
|
* @param mixed $empty Return value of if session target key does not exist
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
**/
|
||||||
|
public function get($key, $empty = '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set session data
|
||||||
|
* This method must be equipped with an automatic start / close.
|
||||||
|
*
|
||||||
|
* @param string $key Target key
|
||||||
|
* @param mixed $data Value
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
**/
|
||||||
|
public function set($key, $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get session data
|
||||||
|
*
|
||||||
|
* @param string $key Target key
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
**/
|
||||||
|
public function remove($key);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,315 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder driver for Volume Group.
|
||||||
|
*
|
||||||
|
* @author Naoki Sawada
|
||||||
|
**/
|
||||||
|
class elFinderVolumeGroup extends elFinderVolumeDriver
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver id
|
||||||
|
* Must be started from letter and contains [a-z0-9]
|
||||||
|
* Used as part of volume id
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
**/
|
||||||
|
protected $driverId = 'g';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* Extend options with required fields
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->options['type'] = 'group';
|
||||||
|
$this->options['path'] = '/';
|
||||||
|
$this->options['dirUrlOwn'] = true;
|
||||||
|
$this->options['syncMinMs'] = 0;
|
||||||
|
$this->options['tmbPath'] = '';
|
||||||
|
$this->options['disabled'] = array(
|
||||||
|
'archive',
|
||||||
|
'copy',
|
||||||
|
'cut',
|
||||||
|
'duplicate',
|
||||||
|
'edit',
|
||||||
|
'empty',
|
||||||
|
'extract',
|
||||||
|
'getfile',
|
||||||
|
'mkdir',
|
||||||
|
'mkfile',
|
||||||
|
'paste',
|
||||||
|
'resize',
|
||||||
|
'rm',
|
||||||
|
'upload'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
/* FS API */
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
/*********************** paths/urls *************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
**/
|
||||||
|
protected function _dirname($path)
|
||||||
|
{
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _basename($path)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _joinPath($dir, $name)
|
||||||
|
{
|
||||||
|
return '/' . $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _normpath($path)
|
||||||
|
{
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _relpath($path)
|
||||||
|
{
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _abspath($path)
|
||||||
|
{
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _path($path)
|
||||||
|
{
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _inpath($path, $parent)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************** file stat ********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _stat($path)
|
||||||
|
{
|
||||||
|
if ($path === '/') {
|
||||||
|
return array(
|
||||||
|
'size' => 0,
|
||||||
|
'ts' => 0,
|
||||||
|
'mime' => 'directory',
|
||||||
|
'read' => true,
|
||||||
|
'write' => false,
|
||||||
|
'locked' => true,
|
||||||
|
'hidden' => false,
|
||||||
|
'dirs' => 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _subdirs($path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _dimensions($path, $mime)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/******************** file/dir content *********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function readlink($path)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _scandir($path)
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _fopen($path, $mode = 'rb')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _fclose($fp, $path = '')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** file/dir manipulations *************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _mkdir($path, $name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _mkfile($path, $name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _symlink($source, $targetDir, $name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _copy($source, $targetDir, $name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _move($source, $targetDir, $name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _unlink($path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _rmdir($path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _save($fp, $dir, $name, $stat)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _getContents($path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _filePutContents($path, $content)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _checkArchivers()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _chmod($path, $mode)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _findSymlinks($path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _extract($path, $arc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
**/
|
||||||
|
protected function _archive($dir, $files, $name, $arc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder driver for trash bin at local filesystem.
|
||||||
|
*
|
||||||
|
* @author NaokiSawada
|
||||||
|
**/
|
||||||
|
class elFinderVolumeTrash extends elFinderVolumeLocalFileSystem
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Driver id
|
||||||
|
* Must be started from letter and contains [a-z0-9]
|
||||||
|
* Used as part of volume id.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
**/
|
||||||
|
protected $driverId = 't';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
// original option of the Trash
|
||||||
|
$this->options['lockEverything'] = false; // Lock all items in the trash to disable delete, move, rename.
|
||||||
|
|
||||||
|
// common options as the volume driver
|
||||||
|
$this->options['alias'] = 'Trash';
|
||||||
|
$this->options['quarantine'] = '';
|
||||||
|
$this->options['rootCssClass'] = 'elfinder-navbar-root-trash';
|
||||||
|
$this->options['copyOverwrite'] = false;
|
||||||
|
$this->options['uiCmdMap'] = array('paste' => 'hidden', 'mkdir' => 'hidden', 'copy' => 'restore');
|
||||||
|
$this->options['disabled'] = array('archive', 'duplicate', 'edit', 'extract', 'mkfile', 'places', 'put', 'rename', 'resize', 'upload');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount(array $opts)
|
||||||
|
{
|
||||||
|
if ($this->options['lockEverything']) {
|
||||||
|
if (!is_array($opts['attributes'])) {
|
||||||
|
$opts['attributes'] = array();
|
||||||
|
}
|
||||||
|
$attr = array(
|
||||||
|
'pattern' => '/./',
|
||||||
|
'locked' => true,
|
||||||
|
);
|
||||||
|
array_unshift($opts['attributes'], $attr);
|
||||||
|
}
|
||||||
|
// force set `copyJoin` to true
|
||||||
|
$opts['copyJoin'] = true;
|
||||||
|
|
||||||
|
return parent::mount($opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elFinder driver for trash bin at MySQL Database
|
||||||
|
*
|
||||||
|
* @author NaokiSawada
|
||||||
|
**/
|
||||||
|
class elFinderVolumeTrashMySQL extends elFinderVolumeMySQL
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Driver id
|
||||||
|
* Must be started from letter and contains [a-z0-9]
|
||||||
|
* Used as part of volume id.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
**/
|
||||||
|
protected $driverId = 'tm';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
// original option of the Trash
|
||||||
|
$this->options['lockEverything'] = false; // Lock all items in the trash to disable delete, move, rename.
|
||||||
|
|
||||||
|
// common options as the volume driver
|
||||||
|
$this->options['alias'] = 'Trash';
|
||||||
|
$this->options['quarantine'] = '';
|
||||||
|
$this->options['rootCssClass'] = 'elfinder-navbar-root-trash';
|
||||||
|
$this->options['copyOverwrite'] = false;
|
||||||
|
$this->options['uiCmdMap'] = array('paste' => 'hidden', 'mkdir' => 'hidden', 'copy' => 'restore');
|
||||||
|
$this->options['disabled'] = array('archive', 'duplicate', 'edit', 'extract', 'mkfile', 'places', 'put', 'rename', 'resize', 'upload');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount(array $opts)
|
||||||
|
{
|
||||||
|
if ($this->options['lockEverything']) {
|
||||||
|
if (!is_array($opts['attributes'])) {
|
||||||
|
$opts['attributes'] = array();
|
||||||
|
}
|
||||||
|
$attr = array(
|
||||||
|
'pattern' => '/./',
|
||||||
|
'locked' => true,
|
||||||
|
);
|
||||||
|
array_unshift($opts['attributes'], $attr);
|
||||||
|
}
|
||||||
|
// force set `copyJoin` to true
|
||||||
|
$opts['copyJoin'] = true;
|
||||||
|
|
||||||
|
return parent::mount($opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
<?php
|
||||||
|
//Module: True File Manager by Sitecreator
|
||||||
|
//Version (Версия): 1.3.1
|
||||||
|
//Developer of this module (Разработчик): Malyutin R. A. (Малютин Р. А.), Developer Website: https://sitecreator.ru 2019-2021(c)
|
||||||
|
//All rights reserved. (с)
|
||||||
|
//Все права защищены. (с)
|
||||||
|
//The copyright for this module belongs to Mr. Malyutin R. A.
|
||||||
|
//Авторские права на данный модуль принадлежат господину Малютину Р. А.
|
||||||
|
//Запрещается распространение данного программного продукта без разрешения автора.
|
||||||
|
//Distribution of this software product without the permission of the author is prohibited.
|
||||||
|
//
|
||||||
|
//Данное программное обеспечение предоставляется на основе простой неисключительной лицензии.
|
||||||
|
//Данное программное обеспечение распространяется по общепринятому принципу "как есть" без предоставления каких-либо гарантий в явной или неявной форме.
|
||||||
|
//Разработчик не гарантирует пригодность данного программного обеспечения для особых целей.
|
||||||
|
//Ни при каких обстоятельствах разработчик не несет ответственности за любой ущерб, который может возникнуть в результате использования данного программного обеспечения.
|
||||||
|
//This software is provided based on a simple, non-exclusive license.
|
||||||
|
//This software is distributed on a generally accepted “as is” basis without any warranty, expressed or implied.
|
||||||
|
//The developer does not guarantee the suitability of this software for special purposes.
|
||||||
|
//Under no circumstances shall the developer be liable for any damage that may result from the use of this software.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//This software uses freely distributed software of 3 parties, namely the following software:
|
||||||
|
//Данное программное обеспечение использует в своем составе свободно распространяемое программное обеспечение 3-х сторон, а именно следующий софт:
|
||||||
|
//
|
||||||
|
//jquery ver. 3.4.1 (JavaScript library), Developer Website: https://jquery.com/
|
||||||
|
//jquery ui ver. 1.12.1 (JavaScript library), Developer Website: https://jqueryui.com/
|
||||||
|
//elFinder ver. 2.1.49, Developer Website: https://jqueryui.com/
|
||||||
|
//
|
||||||
|
//На указанное выше программное обеспечение третьих лиц распространяется авторское право, принадлежащее соответственно этим третьим лицам.
|
||||||
|
//Подробнее об этих авторских правах можно прочитать на соответствующих вебсайтах разработчиков. Адреса соответствующих вебсайтов указаны выше.
|
||||||
|
//
|
||||||
|
//The above third-party software is copyrighted by these third parties, respectively.
|
||||||
|
//You can read more about these copyrights on their respective developer websites. The addresses of the respective websites are indicated above.
|
||||||
|
|
||||||
|
|
||||||
|
class ControllerCommonFileManager extends ControllerCommonFileManager__ {
|
||||||
|
public function index() {
|
||||||
|
$oc23 = (version_compare(VERSION, "2.3", ">="))? true:false;
|
||||||
|
$oc15 = (version_compare(VERSION, "2.0", "<"))? true:false;
|
||||||
|
$oc30 = (version_compare(VERSION, "3.0", ">="))? true:false;
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
$data['module_ver'] = '1.3.1';
|
||||||
|
|
||||||
|
if($oc30) $data['token'] = $this->session->data['user_token']; // opencart 3
|
||||||
|
else $data['token'] = $this->session->data['token'];
|
||||||
|
|
||||||
|
// Return the target ID for the file manager to set the value
|
||||||
|
if (isset($this->request->get['target'])) {
|
||||||
|
$data['target'] = $this->request->get['target'];
|
||||||
|
} else {
|
||||||
|
$data['target'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// CKEditor
|
||||||
|
if (isset($this->request->get['cke'])) {
|
||||||
|
$data['cke'] = $this->request->get['cke'];
|
||||||
|
} else {
|
||||||
|
$data['cke'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// summernote_id
|
||||||
|
if (isset($this->request->get['summernote_id'])) {
|
||||||
|
$data['summernote_id'] = $this->request->get['summernote_id'];
|
||||||
|
} else {
|
||||||
|
$data['summernote_id'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the thumbnail for the file manager to show a thumbnail
|
||||||
|
if (isset($this->request->get['thumb'])) {
|
||||||
|
$data['thumb'] = $this->request->get['thumb'];
|
||||||
|
} else {
|
||||||
|
$data['thumb'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tpl = "truefilemanager.tpl";
|
||||||
|
if($oc23) $tpl = "truefilemanager";
|
||||||
|
if($oc30) $tpl = "truefilemanager_for_oc3";
|
||||||
|
$tpl = 'extension/module/'. $tpl;
|
||||||
|
|
||||||
|
$this->response->setOutput($this->load->view($tpl, $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connector() {
|
||||||
|
|
||||||
|
require_once DIR_SYSTEM. "library/sitecreator/elFinder/php/elFinderConnector.class.php";
|
||||||
|
require_once DIR_SYSTEM. "library/sitecreator/elFinder/php/elFinder.class.php";
|
||||||
|
require_once DIR_SYSTEM. "library/sitecreator/elFinder/php/elFinderVolumeDriver.class.php";
|
||||||
|
require_once DIR_SYSTEM. "library/sitecreator/elFinder/php/elFinderVolumeLocalFileSystem.class.php";
|
||||||
|
|
||||||
|
if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1')))
|
||||||
|
$domain = (defined('HTTPS_CATALOG'))? HTTPS_CATALOG: HTTPS_SERVER;
|
||||||
|
else {$domain = (defined('HTTP_CATALOG'))? HTTP_CATALOG: HTTP_SERVER;}
|
||||||
|
|
||||||
|
$this->load->model('setting/setting');
|
||||||
|
$img_opti_status = $this->config->get('module_img_opti_status');
|
||||||
|
$upload_allow = array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/pjpeg', 'image/x-png');
|
||||||
|
if ($img_opti_status) {
|
||||||
|
$upload_allow[] = 'image/webp';
|
||||||
|
}
|
||||||
|
|
||||||
|
$opts = array(
|
||||||
|
'roots' => array(
|
||||||
|
array(
|
||||||
|
'driver' => 'LocalFileSystem', // driver for accessing file system (REQUIRED)
|
||||||
|
'path' => DIR_IMAGE. 'catalog/', // path to files (REQUIRED)
|
||||||
|
'URL' => $domain. 'image/catalog/',
|
||||||
|
'tmbPath'=> DIR_IMAGE. 'elfinder_tmb',
|
||||||
|
'tmbURL' => $domain. 'image/elfinder_tmb/',
|
||||||
|
'tmbSize' => 100,
|
||||||
|
'tmbCrop' => false,
|
||||||
|
'tmbBgColor' => '#ffffff',
|
||||||
|
'mimeDetect' => 'internal',
|
||||||
|
'imgLib' => 'auto',
|
||||||
|
'winHashFix' => DIRECTORY_SEPARATOR !== '/', // to make hash same to Linux one on windows too
|
||||||
|
'uploadAllow' => $upload_allow,
|
||||||
|
'uploadDeny' => array('all'),
|
||||||
|
'uploadOrder' => array('allow, deny'),
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$connector = new elFinderConnector(new elFinder($opts), true);
|
||||||
|
$connector->run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
Loading…
Reference in New Issue