Compare commits

..

23 Commits

Author SHA1 Message Date
nertyog 22f1e535c7 fix(svg): hide SVG autofix sub-checkbox container when main SVG option is unchecked 2026-08-13 19:40:30 +03:00
nertyog 1656e0e5c8 fix(svg): set default for module_img_opti_support_svg_autofix to 0 (unchecked by default) 2026-08-13 19:37:59 +03:00
nertyog c0ea9a5a1e fix(svg): make SVG autofix sub-checkbox always visible inside SVG card without initial display:none 2026-08-13 19:35:14 +03:00
nertyog ea4cba3194 fix(svg): scope SVG CSS rules strictly to catalog image paths (/image/catalog/), removing artificial theme class overrides 2026-08-13 19:28:48 +03:00
nertyog c24b8d2ba8 fix(svg): isolate SVG CSS rules, add optional SVG autofix sub-setting, and exclude UI/language flags from sizing rules 2026-08-13 19:25:14 +03:00
nertyog 0b9e19eccd release(v1.2.0): finalize stage 7.1 & 7.2 improvements, watermark preview fixes, tab/tool persistence and checkbox setting handlers 2026-08-07 05:36:25 +03:00
nertyog 7277ecc621 fix(settings): explicitly initialize unchecked checkbox settings to 0 in POST handler before saving to DB 2026-08-07 05:33:59 +03:00
nertyog 9d2bd1c2ed fix(ui): prevent cross-tool scan cache leak when switching tools and refreshing page 2026-08-07 05:26:43 +03:00
nertyog 16f855fcf0 feat(ui): persist active tab and tool selection in localStorage across page refreshes 2026-08-07 05:22:31 +03:00
nertyog 2a9e4c3769 fix(watermark): add interactive HTML5 colorpicker, unique cache-busted preview filenames and exact product canvas aspect ratio 2026-08-07 05:16:40 +03:00
nertyog c7eec6d352 fix(watermark): case-insensitive Windows path check, text font size (wm_text_size), and fixed pixel size (wm_size_px) 2026-08-07 05:11:34 +03:00
nertyog be06ede1c3 feat(ui): implement Stage 7 watermark background switcher (product, white, transparent grid) and action button margin polish 2026-08-07 04:58:49 +03:00
nertyog 07e9d9773c fix(ajax): strip UTF-8 BOM headers from all PHP files and rebuild release zip package image_optimizer_nat_OC_20_22_1_2_0.ocmod.zip 2026-08-07 04:55:52 +03:00
nertyog 3e5dc38ce3 release: rebuild 100% fresh release zip package image_optimizer_nat_OC_20_22_1_2_0.ocmod.zip with verified disk file parity 2026-08-07 04:46:38 +03:00
nertyog ccf944e750 fix(lang): correct Ukrainian translation for button_online_instruction key 2026-08-07 04:44:22 +03:00
nertyog e4307b27fc release: lock version 1.2.0 across all files, install.xml, and rebuild release zip package image_optimizer_nat_OC_20_22_1_2_0.ocmod.zip 2026-08-07 04:36:17 +03:00
nertyog 082fe99fc6 style: improve category badge contrast, padding, and border-radius in light/dark themes 2026-08-07 04:34:20 +03:00
nertyog b7b476e472 docs: add exhaustive documentation table for all 19 pro tools across instruction.html, instructions.txt, and readme.txt 2026-08-07 04:32:44 +03:00
nertyog 7ba8d48fc4 docs: update v1.2.0 badge and document On-the-Fly Fake JPG Guard in instruction.html 2026-08-07 04:31:25 +03:00
nertyog cbf05b88b0 docs: expand documentation to cover all 9 tabs, nuances, and rebuild release packages v1.2.0 2026-08-07 04:29:18 +03:00
nertyog 57d6bac47c docs: update version tag to v1.2.0, sync all documentation files and rebuild release zip package 2026-08-07 04:27:09 +03:00
nertyog c37d0c04b8 docs & release: add online instruction button, clean code comments, update documentation files and build docs/ package structure 2026-08-07 04:24:48 +03:00
nertyog faf03e20b7 release: 1.0.0 reference release for OpenCart 2.0-2.2 (with GD/Imagick labels, On-the-Fly Fake JPG Guard, persistent DB stats, full settings export/import, and UI polish) 2026-08-07 04:16:02 +03:00
51 changed files with 12671 additions and 1763 deletions

3908
docs/instruction.html Normal file

File diff suppressed because one or more lines are too long

87
docs/instructions.txt Normal file
View File

@ -0,0 +1,87 @@
================================================================================
Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0
================================================================================
Онлайн веб-інструкція / Онлайн веб-инструкция / Online Documentation:
http://oc3-demo.nat.od.ua/instructions/image_optimizer_nat_instruction.html
--------------------------------------------------------------------------------
🇺🇦 УКРАЇНСЬКА МОВА (UKRAINIAN)
--------------------------------------------------------------------------------
ВСТАНОВЛЕННЯ ТА ОНОВЛЕННЯ:
1. Розпакуйте архів.
2. Завантажте вміст папки upload/ у корінь вашого сайту по FTP.
3. Перейдіть в "Система" -> "Користувачі" -> "Групи користувачів", виберіть группу (Administrator) та встановіть галочки прав для: extension/module/img_opti (або module/img_opti для OC 2.0-2.2).
4. Перейдіть у "Модулі / Розширення", знайдіть "Image Optimizer [NAT]" та натисніть "Встановити", потім "Редагувати".
ПОВНИЙ ОГЛЯД ВСІХ 9 ВКЛАДОК ТА ФУНКЦІОНАЛУ:
- Вкладка 1 (Налаштування & Статистика): Вибір движка GD або Imagick (з маркуванням дій), накопичувальна статистика з бази даних сайту з кнопкою [Скинути статистику], 100% експорт/імпорт налаштувань у JSON, правила для окремих папок та кнопка Онлайн Інструкції.
- Вкладка 2 (Стиснення та Ресайз): Вибір папок, режим безпечної копії або заміни оригіналів, макс. розміри, якість (WebP/JPG/PNG), багатопотоковість, пороги економії та симетричні кнопки [ПАУЗА] і [ЗУПИНИТИ] 50/50.
- Вкладка 3 (Очищення від сміття): Пошук невикористовуваних зображень в БД та коді тем (.tpl, .twig, .php, .css, .js), Карантин з відновленням в 1 клік або видалення.
- Вкладка 4 (Формати & Захист): Захист від фейкових JPG на льоту (On-the-Fly Fake JPG Guard з вибором режиму білий JPG / прозорий PNG), WebP на льоту, SVG, Lazy Load (blur-up) та заглушка для битих посилань.
- Вкладка 5 (Інструменти Pro - 19 утиліт):
1. broken_db - Пошук записів БД з відсутніми файлами на диску.
2. broken_files - Пошук битих/корумпованих файлів (0 байт).
3. empty_folders - Видалення порожніх папок в image/catalog.
4. folder_tree - Аналіз розмірів та кількості файлів по папках.
5. html_broken - Пошук битих 404 посилань на фото в HTML-описах.
6. small_photos - Виявлення занадто малих фотографій.
7. duplicates - Пошук точних дублікатів фото по хешу MD5.
8. heavy_files - Знаходження надважких оригіналів (>500KB).
9. cmyk - Сканування CMYK фото з автопереведенням у sRGB.
10. svg_security - Очищення SVG-файлів від XSS та скриптів.
11. black_bg - Відновлення білого фону (Edge-only floodfill) на фото з чорними рамками.
12. watermark - Пакетне накладення водяних знаків.
13. placeholders - Пакетне призначення заглушок.
14. png_jpg - Конвертація важких PNG у компактний JPG/WebP.
15. exif - Видалення EXIF-метаданих (GPS, камера) для економії 10-15%.
16. smart_cache - Розумне очищення кешу зображень OpenCart.
17. sanitizer - Очищення імен файлів від кирилиці, пропусків та спецсимволів.
18. translit - Транслітерація кириличних назв фото у латиницю.
19. cache_restore - Відновлення видалених оригіналів з кешу високої роздільної здатності.
- Вкладка 6 (Водяні знаки): Динамічне накладання водяних знаків (зображення або текст) на льоту.
- Вкладка 7 (Заглушки): Автопідстановка зображень-заглушок.
- Вкладка 8 (Крон-завдання): Фоновий запуск стиснення, очищення та інструментів за розкладом.
- Вкладка 9 (Інфо & Екосистема): Графік диска, контакти та кнопка Інструкції.
--------------------------------------------------------------------------------
🇷🇺 РУССКИЙ ЯЗЫК (RUSSIAN)
--------------------------------------------------------------------------------
УСТАНОВКА И ОБНОВЛЕНИЕ:
1. Распакуйте архив.
2. Загрузите содержимое папки upload/ в корень вашего сайта по FTP.
3. Перейдите в "Система" -> "Пользователи" -> "Группы пользователей", выберите группу (Administrator) и установите галочки прав для: extension/module/img_opti (или module/img_opti для OC 2.0-2.2).
4. Перейдите в "Модули / Расширения", найдите "Image Optimizer [NAT]", нажмите "Установить", затем "Редактировать".
ПОЛНЫЙ ОБЗОР ВСЕХ 9 ВКЛАДОК И ФУНКЦИОНАЛА (19 УТИЛИТ):
- Вкладка 1 (Настройки & Статистика): Выбор движка GD или Imagick, накопительная статистика из базы данных с кнопкой [Сбросить статистику], 100% экспорт/импорт настроек в JSON, правила для отдельных папок и кнопка Онлайн Инструкции.
- Вкладка 2 (Сжатие и Ресайз): Выбор папок каталога, режим копии или замены оригиналов, наглядные 50/50 кнопки [ПАУЗА] и [ЗУПИНИТИ].
- Вкладка 3 (Очистка от мусора): Поиск неиспользуемых фото в БД и файлах тем (.tpl, .twig, .php, .css, .js), безопасный Карантин или удаление.
- Вкладка 4 (Форматы & Защита): Защита от фейковых JPG на лету (On-the-Fly Fake JPG Guard), WebP на лету, SVG, Lazy Load (blur-up), редирект битых ссылок.
- Вкладка 5 (Инструменты Pro - 19 утилит): broken_db, broken_files, empty_folders, folder_tree, html_broken, small_photos, duplicates (MD5), heavy_files, cmyk (sRGB), svg_security, black_bg (floodfill), watermark, placeholders, png_jpg, exif, smart_cache, sanitizer, translit, cache_restore.
- Вкладка 6 (Водяные знаки): Наложение водяных знаков (текст/фото) на лету.
- Вкладка 7 (Заглушки): Автозамена отсутствующих фото товаров, категорий и брендов.
- Вкладка 8 (Крон-задачи): Автоматический фоновый запуск по расписанию (ежедневно, еженедельно, ежемесячно).
- Вкладка 9 (Инфо & Экосистема): График использования диска, контакты поддержки и кнопка Онлайн Инструкции.
--------------------------------------------------------------------------------
🇬🇧 ENGLISH
--------------------------------------------------------------------------------
INSTALLATION & UPGRADE:
1. Extract the archive.
2. Upload upload/ to your OpenCart root directory via FTP.
3. Set permissions for extension/module/img_opti (or module/img_opti for OC 2.0-2.2).
4. Install "Image Optimizer [NAT]" under Extensions -> Modules.
EXHAUSTIVE 9-TAB OVERVIEW (INCLUDING 19 PRO UTILITIES):
- Tab 1 (Settings & Stats): GD / Imagick engine choice, persistent DB stats with reset button, 100% full JSON export/import, folder rules, online instruction button.
- Tab 2 (Compression): Folder tree selection, Safe Copy vs Replace, dimensions, qualities, 50/50 Pause & Stop controls.
- Tab 3 (Cleaner): Deep scanning of DB tables and theme files (.tpl, .twig, .php, .css, .js), safe Quarantine with 1-click restore.
- Tab 4 (Formats & Guard): On-the-Fly Fake JPG Guard, WebP on-the-fly, SVG, Lazy Load (blur-up), broken image fallback.
- Tab 5 (Tools Pro - 19 Utilities): broken_db, broken_files, empty_folders, folder_tree, html_broken, small_photos, duplicates (MD5), heavy_files, cmyk (sRGB), svg_security, black_bg (edge floodfill), watermark, placeholders, png_jpg, exif, smart_cache, sanitizer, translit, cache_restore.
- Tab 6 (Watermarks): Dynamic on-the-fly image and text overlays.
- Tab 7 (Placeholders): Fallback placeholder replacement.
- Tab 8 (Cron Scheduler): Automated background runner for daily, weekly, or monthly scheduled jobs.
- Tab 9 (Info & Ecosystem): Disk space chart, support contacts, online instruction button.

96
docs/readme.txt Normal file
View File

@ -0,0 +1,96 @@
================================================================================
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
Online Web Documentation: http://oc3-demo.nat.od.ua/instructions/image_optimizer_nat_instruction.html
--------------------------------------------------------------------------------
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, meaning you can modify it for your own needs. However, you may not resell, redistribute, share, or publish this extension in any form.
--------------------------------------------------------------------------------
INSTALLATION & UPGRADE
--------------------------------------------------------------------------------
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 Administrator and check both "Access" and "Modify" permissions for extension/module/img_opti (or module/img_opti on OC 2.0-2.2).
4. Go to Extensions -> Extensions -> Modules. Find "Image Optimizer [NAT]" and click "Install", then "Edit".
--------------------------------------------------------------------------------
EXHAUSTIVE TAB-BY-TAB MODULE GUIDE
--------------------------------------------------------------------------------
WARNING: Always create a full backup of your /image/catalog/ folder before running any optimizations or deletions!
TAB 1: MODULE SETTINGS & SYSTEM STATUS
- Engine Selection: GD (Standard) or Imagick (Advanced) with explicit GD/Imagick action labelling across all scanning tools.
- Individual Folder Rules: Set per-folder custom JPG/WebP quality and max width/height overrides.
- Disk Usage Chart: Renders a pie chart of folder sizes (Catalog, Cache, Trash) with manual quota limit override.
- Server-side Persistent DB Statistics: Stores total processed files, saved MB, and average savings percentage in site database (oc_setting) with a [Reset Statistics] button.
- Full Settings Export / Import (JSON): Backup and restore 100% of module settings (cron tasks, watermark rules, placeholders, folder rules, formats, limits).
- Online Web-Instruction Button: Direct link to complete web documentation.
TAB 2: OPTIMIZE & RESIZE (COMPRESSION)
- Multi-folder Tree Selection: Select specific subfolders inside image/catalog/.
- Safe Copy vs Replace Originals: Choose between creating a backup copy (catalog_new) or replacing originals directly.
- Dimension Limits & Quality Controls: Max width/height, JPG quality (0-100), PNG quality (0-9), WebP quality (0-100).
- Performance & Multi-threading: Batch chunk size, log screen lines, and multi-threading execution controls.
- Savings Replacement Thresholds: Minimum byte/percent savings required to replace files.
- Interactive Controls: Balanced 50/50 Pause & Stop controls during active processing, plus cache clear button.
TAB 3: UNUSED IMAGES CLEANER (JUNK REMOVAL)
- Deep DB & Theme File Scanning: Scans product, category, manufacturer, banner images, and checks theme files (.tpl, .twig, .php, .css, .js) to prevent accidental deletion of active graphics.
- Safe Quarantine & permanent deletion: Move unused files to image/catalog_trash/ with one-click restore capability, or delete permanently.
TAB 4: IMAGE FORMATS & PROTECTION
- Support SVG & WebP: Enable native SVG uploading and WebP conversion (including TrueFileManager integration).
- WebP "On-the-Fly": Generates WebP images dynamically when requested by storefront browsers.
- Progressive Lazy Load: Applies blur-up progressive image loading on product lists.
- On-the-Fly Fake JPG Guard: Binary PNG header detection inside .jpg extension to eliminate black background glitches on the storefront. Mode dropdown toggle: Convert to white JPG vs save as transparent PNG.
- Fallback Image: Global placeholder image override for broken image links.
TAB 5: TOOLS (PRO) & VISUAL PREVIEW (19 SPECIALIZED UTILITIES)
1. broken_db: Finds DB entries with missing physical files.
2. broken_files: Scans for binary-corrupted or zero-byte truncated images.
3. empty_folders: Scans and purges empty catalog subdirectories.
4. folder_tree: Deep size and file count analysis per subfolder.
5. html_broken: Scans product/category HTML for 404 image links.
6. small_photos: Identifies low-res photos under dimension limits.
7. duplicates: Finds exact duplicate images using MD5 content hashes.
8. heavy_files: Flags raw oversized images exceeding KB limits.
9. cmyk: Detects CMYK color space images and auto-converts to sRGB.
10. svg_security: Strips malicious JS scripts and XSS from SVG files.
11. black_bg: Edge-only floodfill repair of black background borders.
12. watermark: Batch watermarking utility.
13. placeholders: Batch placeholder assignment for missing assets.
14. png_jpg: Mass converts heavy PNGs to JPG/WebP and updates DB.
15. exif: Strips camera/GPS metadata for 10-15% size reduction.
16. smart_cache: Selective expired image cache purger.
17. sanitizer: Cleans file paths of spaces, Cyrillic, and illegal chars.
18. translit: Transliterates Cyrillic filenames to SEO Latin.
19. cache_restore: Recovers deleted original photos from high-res cache.
TAB 6: WATERMARKS
- Dynamic On-the-Fly Overlays: Apply image or text watermarks dynamically on products, categories, brands, or banners.
- Customization: Position, opacity, angle, TTF font, size scaling, and category exclusion/inclusion rules.
TAB 7: PLACEHOLDERS
- Automatic fallback image replacement for products, categories, and brands without an assigned image.
TAB 8: CRON BACKGROUND SCHEDULER
- Automated Background Runner: Set up scheduled cron tasks (daily, weekly, monthly) for compression, junk cleaning, name sanitizing, and black background fixing without manual admin interaction.
TAB 9: INFO & ECOSYSTEM [NAT]
- Module suite promotion, support contact info, and online web documentation button.

Binary file not shown.

View File

@ -1,24 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<modification> <modification>
<name>Image Optimizer [NAT] (OC 2.0-2.2)</name> <name>Image Optimizer [NAT]</name>
<id>Image_Optimizer_nat</id> <id>Image_Optimizer_nat</id>
<code>img_opti_nat_od_ua_v1</code> <code>img_opti_nat_v1</code>
<version>1.2.0</version> <version>1.2.0</version>
<author>NAT</author> <author>NAT</author>
<link>mailto:info@nat.od.ua</link> <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"> <file path="admin/controller/common/column_left.php">
<operation error="skip"> <operation error="skip">
<search><![CDATA[if ($this->user->hasPermission('access', 'catalog/product')) {]]></search> <search><![CDATA[if ($this->user->hasPermission('access', 'catalog/product')) {]]></search>
@ -27,33 +15,45 @@
if ($this->config->get('module_img_opti_status')) { if ($this->config->get('module_img_opti_status')) {
$nat_pos = $this->config->get('module_img_opti_menu_position'); $nat_pos = $this->config->get('module_img_opti_menu_position');
if ($nat_pos == 'sidebar' || $nat_pos == 'both') { if ($nat_pos == 'sidebar' || $nat_pos == 'both') {
if ($this->user->hasPermission('access', 'module/img_opti')) { if ($this->user->hasPermission('access', 'extension/module/img_opti')) {
$nat_item = array( $nat_item = array(
'name' => 'Image Optimizer', 'name' => 'Image Optimizer',
'href' => $this->url->link('module/img_opti', 'token=' . $this->session->data['token'], true), 'href' => $this->url->link('extension/module/img_opti', 'user_token=' . $this->session->data['user_token'], true),
'children' => array() 'children' => array()
); );
$nat_menu_index = -1; $nat_menu_index = -1;
if (isset($data['menus'])) { if (isset($data['menus'])) {
foreach ($data['menus'] as $k => $menu) { foreach ($data['menus'] as $k => $menu) {
if ($menu['id'] == 'menu-nat') { if (isset($menu['id']) && $menu['id'] == 'menu-nat') {
$nat_menu_index = $k; $nat_menu_index = $k;
break; break;
} }
} }
} }
if ($nat_menu_index > -1) { $exists = false;
$data['menus'][$nat_menu_index]['children'][] = $nat_item; if ($nat_menu_index > -1 && isset($data['menus'][$nat_menu_index]['children'])) {
} else { foreach ($data['menus'][$nat_menu_index]['children'] as $child) {
$data['menus'][] = array( if (isset($child['href']) && strpos($child['href'], 'route=extension/module/img_opti') !== false) {
'id' => 'menu-nat', $exists = true;
'icon' => 'fa-rocket fw', break;
'name' => '[NAT] Modules', }
'href' => '', }
'children' => array($nat_item) }
);
if (!$exists) {
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)
);
}
} }
} }
} }
@ -73,11 +73,20 @@
if ($this->config->get('module_img_opti_status')) { if ($this->config->get('module_img_opti_status')) {
$nat_pos = $this->config->get('module_img_opti_menu_position'); $nat_pos = $this->config->get('module_img_opti_menu_position');
if ($nat_pos == 'header' || $nat_pos == 'both') { if ($nat_pos == 'header' || $nat_pos == 'both') {
if ($this->user->hasPermission('access', 'module/img_opti')) { if ($this->user->hasPermission('access', 'extension/module/img_opti')) {
$data['nat_menus'][] = array( $exists = false;
'name' => 'Image Optimizer', foreach ($data['nat_menus'] as $m) {
'href' => $this->url->link('module/img_opti', 'token=' . $this->session->data['token'], true) if (isset($m['href']) && strpos($m['href'], 'route=extension/module/img_opti') !== false) {
); $exists = true;
break;
}
}
if (!$exists) {
$data['nat_menus'][] = array(
'name' => 'Image Optimizer',
'href' => $this->url->link('extension/module/img_opti', 'user_token=' . $this->session->data['user_token'], true)
);
}
} }
} }
} }
@ -85,22 +94,34 @@
</operation> </operation>
</file> </file>
<file path="admin/view/template/common/header.tpl"> <file path="admin/view/template/common/header.twig">
<operation error="skip"> <operation error="skip">
<search><![CDATA[<li><a href="<?php echo $logout; ?>"]]></search> <search><![CDATA[</head>]]></search>
<add position="before"><![CDATA[ <add position="before"><![CDATA[
<?php if (isset($nat_menus) && $nat_menus && !isset($nat_menu_rendered)) { ?> <style>
<?php $nat_menu_rendered = true; ?> a.img-thumbnail img, a.thumbnail img {
width: 100px !important;
height: 100px !important;
object-fit: contain !important;
}
</style>
]]></add>
</operation>
<operation error="skip">
<search><![CDATA[<li><a href="{{ logout }}"]]></search>
<add position="before"><![CDATA[
{% if nat_menus is defined and nat_menus and not nat_menu_rendered|default(false) %}
{% set nat_menu_rendered = true %}
<li class="dropdown" id="nat-ecosystem-menu"> <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> <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"> <ul class="dropdown-menu dropdown-menu-right" id="nat-ecosystem-list">
<li class="dropdown-header">Ecosystem [NAT]</li> <li class="dropdown-header">Ecosystem [NAT]</li>
<?php foreach ($nat_menus as $n_menu) { ?> {% for n_menu in nat_menus %}
<li><a href="<?php echo $n_menu['href']; ?>"><i class="fa fa-angle-right"></i> <?php echo $n_menu['name']; ?></a></li> <li><a href="{{ n_menu.href }}"><i class="fa fa-angle-right"></i> {{ n_menu.name }}</a></li>
<?php } ?> {% endfor %}
</ul> </ul>
</li> </li>
<?php } ?> {% endif %}
]]></add> ]]></add>
</operation> </operation>
</file> </file>
@ -162,6 +183,12 @@
<search><![CDATA[public function resize($filename, $width, $height) {]]></search> <search><![CDATA[public function resize($filename, $width, $height) {]]></search>
<add position="after"><![CDATA[ <add position="after"><![CDATA[
$GLOBALS['registry'] = $this->registry; $GLOBALS['registry'] = $this->registry;
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_auto_fake_jpg_guard') && !empty($filename)) {
$this->load->model('extension/module/img_opti');
try {
$filename = $this->model_extension_module_img_opti->checkAndFixFakeJpgOnTheFly($filename);
} catch (\Exception $e) {}
}
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) { 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')) { 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'); $fallback_image = $this->config->get('module_img_opti_fallback_image');
@ -194,9 +221,9 @@
<search><![CDATA[$image->save(DIR_IMAGE . $image_new);]]></search> <search><![CDATA[$image->save(DIR_IMAGE . $image_new);]]></search>
<add position="after"><![CDATA[ <add position="after"><![CDATA[
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) { if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
$this->load->model('module/img_opti'); $this->load->model('extension/module/img_opti');
try { try {
$this->model_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename); $this->model_extension_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
} catch (\Exception $e) {} } catch (\Exception $e) {}
} }
]]></add> ]]></add>
@ -208,9 +235,9 @@
<add position="replace"><![CDATA[copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new); <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')) { if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
$this->load->model('module/img_opti'); $this->load->model('extension/module/img_opti');
try { try {
$this->model_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename); $this->model_extension_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
} catch (\Exception $e) {} } catch (\Exception $e) {}
} }
}]]></add> }]]></add>
@ -222,6 +249,12 @@
<search><![CDATA[public function resize($filename, $width, $height) {]]></search> <search><![CDATA[public function resize($filename, $width, $height) {]]></search>
<add position="after"><![CDATA[ <add position="after"><![CDATA[
$GLOBALS['registry'] = $this->registry; $GLOBALS['registry'] = $this->registry;
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_auto_fake_jpg_guard') && !empty($filename)) {
$this->load->model('extension/module/img_opti');
try {
$filename = $this->model_extension_module_img_opti->checkAndFixFakeJpgOnTheFly($filename);
} catch (\Exception $e) {}
}
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) { 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')) { 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'); $fallback_image = $this->config->get('module_img_opti_fallback_image');
@ -251,7 +284,7 @@
</file> </file>
<file path="system/library/image.php"> <file path="system/library/image.php">
<operation error="skip"> <operation>
<search><![CDATA[if ($this->mime == 'image/gif') {]]></search> <search><![CDATA[if ($this->mime == 'image/gif') {]]></search>
<add position="before"><![CDATA[ <add position="before"><![CDATA[
if (($this->mime == 'image/webp' || $this->mime == 'image/vnd.webp' || $this->mime == 'image/x-webp') && function_exists('imagecreatefromwebp')) { if (($this->mime == 'image/webp' || $this->mime == 'image/vnd.webp' || $this->mime == 'image/x-webp') && function_exists('imagecreatefromwebp')) {
@ -259,7 +292,7 @@
} else } else
]]></add> ]]></add>
</operation> </operation>
<operation error="skip"> <operation>
<search><![CDATA[if ($extension == 'jpeg' || $extension == 'jpg') {]]></search> <search><![CDATA[if ($extension == 'jpeg' || $extension == 'jpg') {]]></search>
<add position="before"><![CDATA[ <add position="before"><![CDATA[
if ($extension == 'webp' && function_exists('imagewebp')) { if ($extension == 'webp' && function_exists('imagewebp')) {
@ -267,7 +300,7 @@
} else } else
]]></add> ]]></add>
</operation> </operation>
<operation error="skip"> <operation>
<search><![CDATA[imagejpeg($this->image, $file, $quality);]]></search> <search><![CDATA[imagejpeg($this->image, $file, $quality);]]></search>
<add position="before"><![CDATA[ <add position="before"><![CDATA[
@imageinterlace($this->image, true); @imageinterlace($this->image, true);
@ -276,299 +309,9 @@
</file> </file>
<file path="system/library/response.php"> <file path="system/library/response.php">
<operation error="skip"> <operation>
<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> <search><![CDATA[echo $output;]]></search>
<add position="replace"><![CDATA[ <add position="before"><![CDATA[
$registry = isset($GLOBALS['registry']) ? $GLOBALS['registry'] : null; $registry = isset($GLOBALS['registry']) ? $GLOBALS['registry'] : null;
if ($registry && !defined('DIR_CATALOG')) { if ($registry && !defined('DIR_CATALOG')) {
$config = $registry->get('config'); $config = $registry->get('config');
@ -577,6 +320,8 @@ document.addEventListener("DOMContentLoaded", function() {
$lazy_load = $config->get('module_img_opti_lazy_load'); $lazy_load = $config->get('module_img_opti_lazy_load');
$broken_fallback = $config->get('module_img_opti_broken_fallback'); $broken_fallback = $config->get('module_img_opti_broken_fallback');
$fallback_image = $config->get('module_img_opti_fallback_image'); $fallback_image = $config->get('module_img_opti_fallback_image');
$support_svg = $config->get('module_img_opti_support_svg');
$support_svg_autofix = ($config->get('module_img_opti_support_svg_autofix') !== null) ? $config->get('module_img_opti_support_svg_autofix') : 0;
$theme = $config->get('config_theme'); $theme = $config->get('config_theme');
$additional_width = 0; $additional_width = 0;
@ -606,43 +351,93 @@ document.addEventListener("DOMContentLoaded", function() {
if (!$thumb_width) { $thumb_width = 228; } if (!$thumb_width) { $thumb_width = 228; }
if (!$thumb_height) { $thumb_height = 228; } if (!$thumb_height) { $thumb_height = 228; }
$svg_style = '<style>' . "\n" . if ($support_svg && $support_svg_autofix) {
'img[src*=".svg"], img[src*=".SVG"] {' . "\n" . $svg_style = '<style>' . "\n" .
' max-width: 100% !important;' . "\n" . '.product-thumb .image img[src*=".svg"], .product-thumb .image img[src*=".SVG"], ' . "\n" .
' max-height: 100% !important;' . "\n" . '.thumbnail img[src*=".svg"], .thumbnail img[src*=".SVG"], ' . "\n" .
' object-fit: contain !important;' . "\n" . '.image-additional img[src*=".svg"], .image-additional img[src*=".SVG"], ' . "\n" .
'}' . "\n" . 'img[src*="/image/catalog/"][src*=".svg"], img[src*="/image/catalog/"][src*=".SVG"] {' . "\n" .
'img.mfp-img[src*=".svg"], img.mfp-img[src*=".SVG"], #cboxLoadedContent img[src*=".svg"] {' . "\n" . ' max-width: 100% !important;' . "\n" .
' width: 800px !important;' . "\n" . ' max-height: 100% !important;' . "\n" .
' max-width: 100% !important;' . "\n" . ' object-fit: contain !important;' . "\n" .
' height: auto !important;' . "\n" . '}' . "\n" .
'}' . "\n" . 'img.mfp-img[src*=".svg"], img.mfp-img[src*=".SVG"], #cboxLoadedContent img[src*=".svg"] {' . "\n" .
'.thumbnails img[src*=".svg"], .thumbnails img[src*=".SVG"], .thumbnail img[src*=".svg"], .thumbnail img[src*=".SVG"] {' . "\n" . ' width: 800px !important;' . "\n" .
' max-width: 100% !important;' . "\n" . ' max-width: 100% !important;' . "\n" .
' max-height: 100% !important;' . "\n" . ' height: auto !important;' . "\n" .
' width: ' . $thumb_width . 'px !important;' . "\n" . '}' . "\n" .
' height: ' . $thumb_height . 'px !important;' . "\n" . '.thumbnails img[src*=".svg"], .thumbnails img[src*=".SVG"] {' . "\n" .
' object-fit: contain !important;' . "\n" . ' max-width: 100% !important;' . "\n" .
'}' . "\n" . ' max-height: 100% !important;' . "\n" .
'.image-additional img[src*=".svg"], .image-additional img[src*=".SVG"] {' . "\n" . ' width: ' . $thumb_width . 'px !important;' . "\n" .
' width: ' . $additional_width . 'px !important;' . "\n" . ' height: ' . $thumb_height . 'px !important;' . "\n" .
' height: ' . $additional_height . 'px !important;' . "\n" . ' object-fit: contain !important;' . "\n" .
' max-width: 100% !important;' . "\n" . '}' . "\n" .
' max-height: 100% !important;' . "\n" . '</style>';
' object-fit: contain !important;' . "\n" . $output = str_ireplace('</head>', $svg_style . '</head>', $output);
'}' . "\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) { if ($webp_on_fly || $lazy_load || $broken_fallback) {
$registry->get('load')->model('tool/image'); $registry->get('load')->model('tool/image');
$model_tool_image = $registry->get('model_tool_image'); $model_tool_image = $registry->get('model_tool_image');
if ($webp_on_fly) {
$output = preg_replace_callback('/<a\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly) {
$attrs_str = $matches[1];
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;
}
if (!empty($attrs['href'])) {
$href = $attrs['href'];
$rel_path = '';
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png))/is', $href, $m_rel)) {
$rel_path = urldecode($m_rel[1]);
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png))/is', $href, $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 ($browser_supports_webp && $clean_rel_path) {
$ext = strtolower(substr(strrchr($clean_rel_path, '.'), 1));
$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)) {
$attrs['href'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['href']);
}
}
}
}
$new_attrs = array();
foreach ($attrs as $k => $v) {
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
}
return '<a ' . implode(' ', $new_attrs) . '>';
}, $output);
}
$output = preg_replace_callback('/<img\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly, $lazy_load, $broken_fallback, $fallback_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]; $attrs_str = $matches[1];
if (stripos($attrs_str, 'catalog_trash') !== false) { if (stripos($attrs_str, 'catalog_trash') !== false) {
@ -739,74 +534,74 @@ document.addEventListener("DOMContentLoaded", function() {
@imagedestroy($im); @imagedestroy($im);
} }
} }
} }
if (file_exists($s_phys_webp)) { if (file_exists($s_phys_webp)) {
$subparts[0] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $s_url); $subparts[0] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $s_url);
} }
} }
} }
} }
} }
$part = implode(' ', $subparts); $part = implode(' ', $subparts);
} }
$attrs[$srcset_attr] = implode(', ', $parts); $attrs[$srcset_attr] = implode(', ', $parts);
} }
} }
} }
$is_svg = (strtolower(substr(strrchr($clean_rel_path, '.'), 1)) === 'svg'); $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) { if ($lazy_load && $clean_rel_path && is_file(DIR_IMAGE . $clean_rel_path) && !$is_svg) {
try { try {
$tiny_thumb = $model_tool_image->resize($clean_rel_path, 20, 20); $tiny_thumb = $model_tool_image->resize($clean_rel_path, 20, 20);
if ($has_datasrc) { if ($has_datasrc) {
$attrs['src'] = $tiny_thumb; $attrs['src'] = $tiny_thumb;
} else { } else {
$attrs['data-src'] = isset($attrs['src']) ? $attrs['src'] : ''; $attrs['data-src'] = isset($attrs['src']) ? $attrs['src'] : '';
$attrs['src'] = $tiny_thumb; $attrs['src'] = $tiny_thumb;
} }
if (isset($attrs['srcset'])) { if (isset($attrs['srcset'])) {
$attrs['data-srcset'] = $attrs['srcset']; $attrs['data-srcset'] = $attrs['srcset'];
unset($attrs['srcset']); unset($attrs['srcset']);
} }
$class = isset($attrs['class']) ? $attrs['class'] . ' img-lazyload' : 'img-lazyload'; $class = isset($attrs['class']) ? $attrs['class'] . ' img-lazyload' : 'img-lazyload';
$attrs['class'] = $class; $attrs['class'] = $class;
$style = 'filter: blur(10px); transition: filter 0.3s;'; $style = 'filter: blur(10px); transition: filter 0.3s;';
if (isset($attrs['style'])) { if (isset($attrs['style'])) {
$attrs['style'] = rtrim(trim($attrs['style']), ';') . '; ' . $style; $attrs['style'] = rtrim(trim($attrs['style']), ';') . '; ' . $style;
} else { } else {
$attrs['style'] = $style; $attrs['style'] = $style;
} }
} catch (Exception $e) {} } catch (Exception $e) {}
} }
if ($broken_fallback) { if ($broken_fallback) {
$fallback_url = ''; $fallback_url = '';
$req_obj = $registry->get('request'); $req_obj = $registry->get('request');
$is_https = isset($req_obj->server['HTTPS']) && ($req_obj->server['HTTPS'] == 'on' || $req_obj->server['HTTPS'] == '1'); $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'); $server_url = $is_https ? $config->get('config_ssl') : $config->get('config_url');
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) { if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
$fallback_url = $server_url . 'image/' . $fallback_image; $fallback_url = $server_url . 'image/' . $fallback_image;
} elseif ($config->get('config_logo') && is_file(DIR_IMAGE . $config->get('config_logo'))) { } elseif ($config->get('config_logo') && is_file(DIR_IMAGE . $config->get('config_logo'))) {
$fallback_url = $server_url . 'image/' . $config->get('config_logo'); $fallback_url = $server_url . 'image/' . $config->get('config_logo');
} else { } else {
$fallback_url = $server_url . 'image/no_image.png'; $fallback_url = $server_url . 'image/no_image.png';
} }
if ($fallback_url) { if ($fallback_url) {
$attrs['onerror'] = "this.src='" . $fallback_url . "'; this.srcset=''; this.onerror=null;"; $attrs['onerror'] = "this.src='" . $fallback_url . "'; this.srcset=''; this.onerror=null;";
} }
} }
$new_attrs = array(); $new_attrs = array();
foreach ($attrs as $k => $v) { foreach ($attrs as $k => $v) {
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"'; $new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
} }
return '<img ' . implode(' ', $new_attrs) . '>'; return '<img ' . implode(' ', $new_attrs) . '>';
}, $output); }, $output);
if ($lazy_load) { if ($lazy_load) {
$lazy_script = '<style>img.img-lazyload-loaded { filter: blur(0) !important; }</style> $lazy_script = '<style>img.img-lazyload-loaded { filter: blur(0) !important; }</style>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.img-lazyload")); var lazyImages = [].slice.call(document.querySelectorAll("img.img-lazyload"));
@ -846,61 +641,68 @@ document.addEventListener("DOMContentLoaded", function() {
}); });
</script> </script>
</body>'; </body>';
$output = str_ireplace('</body>', $lazy_script, $output); $output = str_ireplace('</body>', $lazy_script, $output);
} }
} }
} }
} }
echo $output;
]]></add> ]]></add>
</operation> </operation>
</file> </file>
<file path="catalog/controller/product/product.php"> <file path="catalog/controller/product/product.php">
<operation error="skip"> <operation error="skip">
<search><![CDATA[if ($product_info['image']) {]]></search> <search><![CDATA[if ($product_info['image']) {
<add position="before"><![CDATA[ $data['popup'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height'));
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')) { } else {
$product_info['image'] = $this->config->get('module_img_opti_fallback_image'); $data['popup'] = '';
}]]></search>
<add position="replace"><![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'))) {
$data['popup'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height'));
} else {
$data['popup'] = '';
}
]]></add>
</operation>
<operation error="skip">
<search><![CDATA[if ($product_info['image']) {
$data['thumb'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_height'));
} else {
$data['thumb'] = '';
}]]></search>
<add position="replace"><![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'))) {
$data['thumb'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_height'));
} else {
$data['thumb'] = '';
} }
]]></add> ]]></add>
</operation> </operation>
</file> </file>
<!-- Inject sidebar menu item on OC 2.0/2.1 (menu.php / menu.tpl) --> <file path="system/framework.php">
<file path="admin/controller/common/menu.php">
<operation error="skip"> <operation error="skip">
<search><![CDATA[$data['module'] = $this->url->link('extension/module']]></search> <search><![CDATA[$registry = new Registry();]]></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[ <add position="after"><![CDATA[
<?php if (isset($module_img_opti)) { ?> $GLOBALS['registry'] = $registry;
<li><a href="<?php echo $module_img_opti; ?>">Image Optimizer</a></li>
<?php } ?>
]]></add> ]]></add>
</operation> </operation>
</file> </file>
<file path="system/library/sitecreator/trueFileManager/ControllerCommonTrueFileManager.php">
<file path="system/library/sitecreator/trueFileManager/ControllerCommonTrueFileManager.php|admin/controller/common/truefilemanager.php" error="skip"> <operation error="skip">
<operation> <search><![CDATA['uploadAllow' => array('image/jpeg', 'image/png','image/webp', 'image/gif', 'image/svg+xml',]]></search>
<search><![CDATA['image/pjpeg', 'image/x-png'),]]></search> <add position="replace" offset="2"><![CDATA['uploadAllow' => ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_support_webp')) ? array('image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/svg+xml', 'image/pjpeg', 'image/x-png') : array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/pjpeg', 'image/x-png'),]]></add>
<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>
<file path="system/library/sitecreator/elFinder/php/elFinderVolumeDriver.class.php">
<operation error="skip">
<search><![CDATA['image/vnd.adobe.photoshop' => 'psd']]></search>
<add position="after"><![CDATA[,
'image/webp' => 'webp',
'image/vnd.webp' => 'webp',
'image/x-webp' => 'webp']]></add>
</operation> </operation>
</file> </file>
</modification> </modification>

87
instructions.txt Normal file
View File

@ -0,0 +1,87 @@
================================================================================
Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0
================================================================================
Онлайн веб-інструкція / Онлайн веб-инструкция / Online Documentation:
http://oc3-demo.nat.od.ua/instructions/image_optimizer_nat_instruction.html
--------------------------------------------------------------------------------
🇺🇦 УКРАЇНСЬКА МОВА (UKRAINIAN)
--------------------------------------------------------------------------------
ВСТАНОВЛЕННЯ ТА ОНОВЛЕННЯ:
1. Розпакуйте архів.
2. Завантажте вміст папки upload/ у корінь вашого сайту по FTP.
3. Перейдіть в "Система" -> "Користувачі" -> "Групи користувачів", виберіть группу (Administrator) та встановіть галочки прав для: extension/module/img_opti (або module/img_opti для OC 2.0-2.2).
4. Перейдіть у "Модулі / Розширення", знайдіть "Image Optimizer [NAT]" та натисніть "Встановити", потім "Редагувати".
ПОВНИЙ ОГЛЯД ВСІХ 9 ВКЛАДОК ТА ФУНКЦІОНАЛУ:
- Вкладка 1 (Налаштування & Статистика): Вибір движка GD або Imagick (з маркуванням дій), накопичувальна статистика з бази даних сайту з кнопкою [Скинути статистику], 100% експорт/імпорт налаштувань у JSON, правила для окремих папок та кнопка Онлайн Інструкції.
- Вкладка 2 (Стиснення та Ресайз): Вибір папок, режим безпечної копії або заміни оригіналів, макс. розміри, якість (WebP/JPG/PNG), багатопотоковість, пороги економії та симетричні кнопки [ПАУЗА] і [ЗУПИНИТИ] 50/50.
- Вкладка 3 (Очищення від сміття): Пошук невикористовуваних зображень в БД та коді тем (.tpl, .twig, .php, .css, .js), Карантин з відновленням в 1 клік або видалення.
- Вкладка 4 (Формати & Захист): Захист від фейкових JPG на льоту (On-the-Fly Fake JPG Guard з вибором режиму білий JPG / прозорий PNG), WebP на льоту, SVG, Lazy Load (blur-up) та заглушка для битих посилань.
- Вкладка 5 (Інструменти Pro - 19 утиліт):
1. broken_db - Пошук записів БД з відсутніми файлами на диску.
2. broken_files - Пошук битих/корумпованих файлів (0 байт).
3. empty_folders - Видалення порожніх папок в image/catalog.
4. folder_tree - Аналіз розмірів та кількості файлів по папках.
5. html_broken - Пошук битих 404 посилань на фото в HTML-описах.
6. small_photos - Виявлення занадто малих фотографій.
7. duplicates - Пошук точних дублікатів фото по хешу MD5.
8. heavy_files - Знаходження надважких оригіналів (>500KB).
9. cmyk - Сканування CMYK фото з автопереведенням у sRGB.
10. svg_security - Очищення SVG-файлів від XSS та скриптів.
11. black_bg - Відновлення білого фону (Edge-only floodfill) на фото з чорними рамками.
12. watermark - Пакетне накладення водяних знаків.
13. placeholders - Пакетне призначення заглушок.
14. png_jpg - Конвертація важких PNG у компактний JPG/WebP.
15. exif - Видалення EXIF-метаданих (GPS, камера) для економії 10-15%.
16. smart_cache - Розумне очищення кешу зображень OpenCart.
17. sanitizer - Очищення імен файлів від кирилиці, пропусків та спецсимволів.
18. translit - Транслітерація кириличних назв фото у латиницю.
19. cache_restore - Відновлення видалених оригіналів з кешу високої роздільної здатності.
- Вкладка 6 (Водяні знаки): Динамічне накладання водяних знаків (зображення або текст) на льоту.
- Вкладка 7 (Заглушки): Автопідстановка зображень-заглушок.
- Вкладка 8 (Крон-завдання): Фоновий запуск стиснення, очищення та інструментів за розкладом.
- Вкладка 9 (Інфо & Екосистема): Графік диска, контакти та кнопка Інструкції.
--------------------------------------------------------------------------------
🇷🇺 РУССКИЙ ЯЗЫК (RUSSIAN)
--------------------------------------------------------------------------------
УСТАНОВКА И ОБНОВЛЕНИЕ:
1. Распакуйте архив.
2. Загрузите содержимое папки upload/ в корень вашего сайта по FTP.
3. Перейдите в "Система" -> "Пользователи" -> "Группы пользователей", выберите группу (Administrator) и установите галочки прав для: extension/module/img_opti (или module/img_opti для OC 2.0-2.2).
4. Перейдите в "Модули / Расширения", найдите "Image Optimizer [NAT]", нажмите "Установить", затем "Редактировать".
ПОЛНЫЙ ОБЗОР ВСЕХ 9 ВКЛАДОК И ФУНКЦИОНАЛА (19 УТИЛИТ):
- Вкладка 1 (Настройки & Статистика): Выбор движка GD или Imagick, накопительная статистика из базы данных с кнопкой [Сбросить статистику], 100% экспорт/импорт настроек в JSON, правила для отдельных папок и кнопка Онлайн Инструкции.
- Вкладка 2 (Сжатие и Ресайз): Выбор папок каталога, режим копии или замены оригиналов, наглядные 50/50 кнопки [ПАУЗА] и [ЗУПИНИТИ].
- Вкладка 3 (Очистка от мусора): Поиск неиспользуемых фото в БД и файлах тем (.tpl, .twig, .php, .css, .js), безопасный Карантин или удаление.
- Вкладка 4 (Форматы & Защита): Защита от фейковых JPG на лету (On-the-Fly Fake JPG Guard), WebP на лету, SVG, Lazy Load (blur-up), редирект битых ссылок.
- Вкладка 5 (Инструменты Pro - 19 утилит): broken_db, broken_files, empty_folders, folder_tree, html_broken, small_photos, duplicates (MD5), heavy_files, cmyk (sRGB), svg_security, black_bg (floodfill), watermark, placeholders, png_jpg, exif, smart_cache, sanitizer, translit, cache_restore.
- Вкладка 6 (Водяные знаки): Наложение водяных знаков (текст/фото) на лету.
- Вкладка 7 (Заглушки): Автозамена отсутствующих фото товаров, категорий и брендов.
- Вкладка 8 (Крон-задачи): Автоматический фоновый запуск по расписанию (ежедневно, еженедельно, ежемесячно).
- Вкладка 9 (Инфо & Экосистема): График использования диска, контакты поддержки и кнопка Онлайн Инструкции.
--------------------------------------------------------------------------------
🇬🇧 ENGLISH
--------------------------------------------------------------------------------
INSTALLATION & UPGRADE:
1. Extract the archive.
2. Upload upload/ to your OpenCart root directory via FTP.
3. Set permissions for extension/module/img_opti (or module/img_opti for OC 2.0-2.2).
4. Install "Image Optimizer [NAT]" under Extensions -> Modules.
EXHAUSTIVE 9-TAB OVERVIEW (INCLUDING 19 PRO UTILITIES):
- Tab 1 (Settings & Stats): GD / Imagick engine choice, persistent DB stats with reset button, 100% full JSON export/import, folder rules, online instruction button.
- Tab 2 (Compression): Folder tree selection, Safe Copy vs Replace, dimensions, qualities, 50/50 Pause & Stop controls.
- Tab 3 (Cleaner): Deep scanning of DB tables and theme files (.tpl, .twig, .php, .css, .js), safe Quarantine with 1-click restore.
- Tab 4 (Formats & Guard): On-the-Fly Fake JPG Guard, WebP on-the-fly, SVG, Lazy Load (blur-up), broken image fallback.
- Tab 5 (Tools Pro - 19 Utilities): broken_db, broken_files, empty_folders, folder_tree, html_broken, small_photos, duplicates (MD5), heavy_files, cmyk (sRGB), svg_security, black_bg (edge floodfill), watermark, placeholders, png_jpg, exif, smart_cache, sanitizer, translit, cache_restore.
- Tab 6 (Watermarks): Dynamic on-the-fly image and text overlays.
- Tab 7 (Placeholders): Fallback placeholder replacement.
- Tab 8 (Cron Scheduler): Automated background runner for daily, weekly, or monthly scheduled jobs.
- Tab 9 (Info & Ecosystem): Disk space chart, support contacts, online instruction button.

View File

@ -1,9 +1,10 @@
================================================================================ ================================================================================
Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0 Image Optimizer [NAT]: Compress originals & remove unused images v1.2.0
================================================================================ ================================================================================
Thank you for purchasing our extension! Thank you for purchasing our extension!
If you have any questions, feel free to contact us: info@nat.od.ua If you have any questions, feel free to contact us: info@nat.od.ua
Online Web Documentation: http://oc3-demo.nat.od.ua/instructions/image_optimizer_nat_instruction.html
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -16,89 +17,80 @@ By purchasing and downloading this extension, you strictly agree to the followin
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. 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. 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. 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. 6. Distribution: The code is open-source, meaning you can modify it for your own needs. However, you may not resell, redistribute, share, or publish this extension in any form.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
INSTALLATION INSTALLATION & UPGRADE
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
1. Extract the archive. 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). 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`. 3. Go to OpenCart Admin -> System -> Users -> User Groups. Select Administrator and check both "Access" and "Modify" permissions for extension/module/img_opti (or module/img_opti on OC 2.0-2.2).
4. Go to Extensions -> Extensions -> Modules. Find "Image Optimizer [NAT]" and click the "Install" (green plus) button. 4. Go to Extensions -> Extensions -> Modules. Find "Image Optimizer [NAT]" and click "Install", then "Edit".
5. Click the "Edit" button to access the module panel.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
HOW TO USE (TABS & FEATURES) EXHAUSTIVE TAB-BY-TAB MODULE GUIDE
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
WARNING: Always create a full backup of your `/image/catalog/` folder before running any optimizations or deletions! WARNING: Always create a full backup of your /image/catalog/ folder before running any optimizations or deletions!
TAB 1: INFO & STATS TAB 1: MODULE SETTINGS & SYSTEM STATUS
- 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. - Engine Selection: GD (Standard) or Imagick (Advanced) with explicit GD/Imagick action labelling across all scanning tools.
- Cumulative Stats: Displays total optimized files and total disk space saved. - Individual Folder Rules: Set per-folder custom JPG/WebP quality and max width/height overrides.
- Disk Usage Chart: Renders a pie chart of folder sizes (Catalog, Cache, Trash) with manual quota limit override.
- Server-side Persistent DB Statistics: Stores total processed files, saved MB, and average savings percentage in site database (oc_setting) with a [Reset Statistics] button.
- Full Settings Export / Import (JSON): Backup and restore 100% of module settings (cron tasks, watermark rules, placeholders, folder rules, formats, limits).
- Online Web-Instruction Button: Direct link to complete web documentation.
TAB 2: OPTIMIZE & RESIZE (COMPRESSION) TAB 2: OPTIMIZE & RESIZE (COMPRESSION)
1. Enter your License Key (Token) in the respective field. - Multi-folder Tree Selection: Select specific subfolders inside image/catalog/.
2. Select the target folders inside `image/catalog/` you want to process. - Safe Copy vs Replace Originals: Choose between creating a backup copy (catalog_new) or replacing originals directly.
3. Choose the Mode: "Create a copy" (Safest) or "Replace originals". - Dimension Limits & Quality Controls: Max width/height, JPG quality (0-100), PNG quality (0-9), WebP quality (0-100).
4. Set your maximum Width/Height and WebP/JPG/PNG quality settings. - Performance & Multi-threading: Batch chunk size, log screen lines, and multi-threading execution controls.
5. Click "START OPTIMIZATION". The module will process files in batches using AJAX without overloading your server. - Savings Replacement Thresholds: Minimum byte/percent savings required to replace files.
- Interactive Controls: Balanced 50/50 Pause & Stop controls during active processing, plus cache clear button.
TAB 3: UNUSED IMAGES CLEANER (JUNK REMOVAL) TAB 3: UNUSED IMAGES CLEANER (JUNK REMOVAL)
1. Select the folders to scan. - Deep DB & Theme File Scanning: Scans product, category, manufacturer, banner images, and checks theme files (.tpl, .twig, .php, .css, .js) to prevent accidental deletion of active graphics.
2. Click "FIND JUNK". The module compares physical files on disk with your Database and Theme files. - Safe Quarantine & permanent deletion: Move unused files to image/catalog_trash/ with one-click restore capability, or delete permanently.
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 TAB 4: IMAGE FORMATS & PROTECTION
- Dynamic Overlay rules: Set up watermarks (image or text) to overlay on category, brand, banner, or product images on-the-fly. - Support SVG & WebP: Enable native SVG uploading and WebP conversion (including TrueFileManager integration).
- Position, transparency, angle, font (TTF), size scaling, and category exclusion/inclusion filters are fully supported. - WebP "On-the-Fly": Generates WebP images dynamically when requested by storefront browsers.
- Progressive Lazy Load: Applies blur-up progressive image loading on product lists.
- On-the-Fly Fake JPG Guard: Binary PNG header detection inside .jpg extension to eliminate black background glitches on the storefront. Mode dropdown toggle: Convert to white JPG vs save as transparent PNG.
- Fallback Image: Global placeholder image override for broken image links.
TAB 5: PLACEHOLDERS TAB 5: TOOLS (PRO) & VISUAL PREVIEW (19 SPECIALIZED UTILITIES)
- Renders fallback images for products, categories, or brands that do not have an image assigned in the database, keeping the storefront look consistent. 1. broken_db: Finds DB entries with missing physical files.
2. broken_files: Scans for binary-corrupted or zero-byte truncated images.
3. empty_folders: Scans and purges empty catalog subdirectories.
4. folder_tree: Deep size and file count analysis per subfolder.
5. html_broken: Scans product/category HTML for 404 image links.
6. small_photos: Identifies low-res photos under dimension limits.
7. duplicates: Finds exact duplicate images using MD5 content hashes.
8. heavy_files: Flags raw oversized images exceeding KB limits.
9. cmyk: Detects CMYK color space images and auto-converts to sRGB.
10. svg_security: Strips malicious JS scripts and XSS from SVG files.
11. black_bg: Edge-only floodfill repair of black background borders.
12. watermark: Batch watermarking utility.
13. placeholders: Batch placeholder assignment for missing assets.
14. png_jpg: Mass converts heavy PNGs to JPG/WebP and updates DB.
15. exif: Strips camera/GPS metadata for 10-15% size reduction.
16. smart_cache: Selective expired image cache purger.
17. sanitizer: Cleans file paths of spaces, Cyrillic, and illegal chars.
18. translit: Transliterates Cyrillic filenames to SEO Latin.
19. cache_restore: Recovers deleted original photos from high-res cache.
TAB 6: IMAGE FORMATS (🧪 BETA) TAB 6: WATERMARKS
- WebP Support: Enable WebP on-the-fly delivery. - Dynamic On-the-Fly Overlays: Apply image or text watermarks dynamically on products, categories, brands, or banners.
- SVG Support: Safe SVG rendering. - Customization: Position, opacity, angle, TTF font, size scaling, and category exclusion/inclusion rules.
- 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) TAB 7: PLACEHOLDERS
- Filename Sanitizer: Clear names of cyrillic letters, spaces, and special symbols in file and directory paths. - Automatic fallback image replacement for products, categories, and brands without an assigned image.
- 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) TAB 8: CRON BACKGROUND SCHEDULER
- Configure automated background directory scans and image compression schedules using a Cron token. - Automated Background Runner: Set up scheduled cron tasks (daily, weekly, monthly) for compression, junk cleaning, name sanitizing, and black background fixing without manual admin interaction.
TAB 9: INFO & ECOSYSTEM [NAT]
-------------------------------------------------------------------------------- - Module suite promotion, support contact info, and online web documentation button.
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

View File

@ -0,0 +1,608 @@
<?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_svg_autofix'] = 'Auto-fix SVG CSS Styles';
$_['help_support_svg_autofix'] = 'Applies isolated CSS sizing to catalog SVG product images without affecting language flags or theme icons.';
$_['entry_support_webp'] = 'WebP Upload Support (+ TrueFileManager)';
$_['help_support_webp'] = 'Allows uploading, selecting, and browsing WebP images via standard FileManager and TrueFileManager (elFinder).';
$_['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!';
$_['entry_auto_fake_jpg_guard'] = 'Fake JPG Guard (On-the-Fly Protection)';
$_['help_auto_fake_jpg_guard'] = 'Automatically detects uploaded/imported PNG files wrongly masked with a .jpg extension (which cause solid black backgrounds on storefront) and fixes them on-the-fly upon first access.';
$_['entry_fake_jpg_mode'] = 'Fake JPG Correction Mode:';
$_['text_fake_jpg_mode_jpg'] = 'Convert to clean JPG with White BG (#FFFFFF) [Recommended]';
$_['text_fake_jpg_mode_png'] = 'Save as real PNG (keep transparency + update DB links)';
$_['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 stored in site DB (accumulates Optimization, Cleaner, Tools & Cron)';
$_['text_reset_stats'] = 'Reset statistics';
$_['text_reset_stats_confirm']= 'Are you sure you want to reset all optimization statistics in DB?';
$_['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';
$_['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:';
$_['entry_cron_batch_limit'] = 'Batch limit per Cron run (files)';
$_['help_cron_batch_limit'] = 'Maximum number of files processed per single Cron run. Default is 100. Recommended 50 to 300 to prevent server execution timeouts.';
$_['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.';
$_['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';
$_['text_tool_svg_security'] = 'SVG Security Sanitizer (XSS/XXE Protection)';
$_['help_tool_svg_security'] = 'Scans SVG vector images in image/catalog/ for dangerous code (scripts, event handlers, XXE entity declarations, foreignObjects, javascript: URIs) and cleans them.';
$_['text_description_svg_security'] = 'SVG Security Sanitizer inspects all SVG files in catalog directories for security vulnerabilities. Cleaning removes malicious scripts and entities while preserving visual SVG artwork.';
$_['text_svg_threats_found'] = '%d security threats found';
$_['text_svg_threat_xxe_entity'] = 'XXE Entity Attack';
$_['text_svg_threat_script_tag'] = 'Script Tag (<script>)';
$_['text_svg_threat_event_handler'] = 'Inline Event Handler (on*)';
$_['text_svg_threat_javascript_uri']= 'JavaScript URI (javascript:)';
$_['text_svg_threat_foreign_object']= 'Foreign Object (<foreignObject>)';
$_['text_svg_threat_embed_object'] = 'Embedded Object (<object>/<iframe>)';
$_['text_svg_threat_css_injection'] = 'CSS Code Injection';
$_['text_svg_clean_success'] = 'Sanitized & Safe';
$_['text_tool_black_bg'] = 'Black Background & Fake JPG Fixer';
$_['help_tool_black_bg'] = 'Detects PNG images masquerading with .jpg extension (from auto-imports) and images with solid black background/borders. Replaces black background with clean white (#FFFFFF).';
$_['text_description_black_bg'] = 'Black Background & Fake JPG Fixer scans catalog photos for black background artifacts caused by broken PNG-to-JPG imports or background cutouts. Applying fix converts fake JPGs and replaces black border fill with white.';
$_['entry_bb_darkness'] = 'Darkness Threshold (0-100)';
$_['entry_bb_threshold'] = 'Perimeter Black Pixels (%)';
$_['entry_bb_limit'] = 'Scan Limit (files)';
$_['entry_bb_scan_fake'] = 'Scan for Fake JPG (PNG in .jpg)';
$_['entry_bb_scan_black'] = 'Scan for Black Background';
$_['text_bb_fake_jpg'] = 'Fake JPG (PNG binary in .jpg file)';
$_['text_bb_black_bg'] = 'Black Background Detected';
$_['text_bb_fix_flatten_white'] = 'Convert to JPG (white background)';
$_['text_bb_fix_convert_png'] = 'Save as PNG (keep transparency)';
$_['text_bb_fix_white'] = 'Replace black background (GD: edges only)';
$_['text_bb_fix_white_all'] = 'Replace ALL black pixels (GD: aggressive)';
$_['text_bb_fix_white_smooth'] = 'Replace ALL black pixels (GD: Defringe + Smooth)';
$_['text_bb_fix_white_imagick'] = 'Replace black background (Imagick: edges only)';
$_['text_bb_fix_white_all_imagick'] = 'Replace ALL black pixels (Imagick: Fuzzy Lab)';
$_['text_bb_fix_white_smooth_imagick'] = 'Replace ALL black pixels (Imagick: Defringe + Smooth)';
$_['text_bb_type_fake'] = 'Fake JPG Import';
$_['text_bb_type_black'] = 'Black Background';
$_['button_online_instruction'] = 'Module Documentation (Online)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Total Hosting Disk Space (GB)';
$_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)'; $_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)';
$_['entry_auto_disk'] = 'Auto-update on page load'; $_['entry_auto_disk'] = 'Auto-update on page load';
// Cron Tasks
$_['entry_cron_task_name'] = 'Task Name'; $_['entry_cron_task_name'] = 'Task Name';
$_['entry_cron_task_summary'] = 'Compression settings'; $_['entry_cron_task_summary'] = 'Compression settings';
$_['button_add_cron_task'] = 'Add Cron Task'; $_['button_add_cron_task'] = 'Add Cron Task';
$_['button_copy_cron_url'] = 'Copy URL'; $_['button_copy_cron_url'] = 'Copy URL';
$_['text_cron_recommended_url'] = 'Cron task execution URL:'; $_['text_cron_recommended_url'] = 'Cron task execution URL:';
// Licensing
$_['text_license_required'] = 'License Key Required'; $_['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.'; $_['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.'; $_['entry_token_desc'] = 'Enter the activation token issued for your root domain. You can obtain it from your purchase page or by contacting support.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Activate Module';
$_['text_support'] = 'Technical Support'; $_['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.'; $_['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_width'] = 'Watermark Max Width (px)';
$_['entry_wm_max_height'] = 'Watermark Max Height (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_width'] = 'If the source watermark image exceeds this width, it will be automatically scaled down. Set 0 or leave empty for default 800px.';
@ -563,3 +560,7 @@ $_['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.'; $_['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'; $_['text_disk_settings_title'] = 'Disk Limits & Scanning Settings';
$_['button_online_instruction'] = 'Module Documentation (Online)';

View File

@ -265,8 +265,6 @@ $_['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.'; $_['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'; $_['entry_support_webp'] = 'WebP Upload Support';
$_['help_support_webp'] = 'Allows manually uploading, linking, and displaying ready .webp files on the website via the Filemanager.'; $_['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'; $_['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_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_broken_db'] = 'Finds images in the database whose physical files have been deleted from the server. Clears broken links.';
@ -463,7 +461,6 @@ $_['text_reset_cache_hint'] = 'Reset optimization cache (force full rescan)';
$_['confirm_reset_cache'] = 'Reset optimization cache? Next scan will check ALL files again.'; $_['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.'; $_['text_cache_cleared'] = 'Optimization cache cleared. Next scan will check all files.';
$_['error_wm_preview'] = 'Failed to generate watermark preview.'; $_['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_apply_errors_warning'] = 'WARNING: Some files could not be replaced (locked or permission denied):';
$_['text_action_compress'] = 'Compress Images'; $_['text_action_compress'] = 'Compress Images';
$_['text_estimated'] = 'Estimated'; $_['text_estimated'] = 'Estimated';
@ -539,27 +536,12 @@ $_['entry_disk_quota_total'] = 'Total Hosting Disk Space (GB)';
$_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)'; $_['entry_disk_quota_used'] = 'Total Hosting Used Disk Space (GB)';
$_['entry_auto_disk'] = 'Auto-update on page load'; $_['entry_auto_disk'] = 'Auto-update on page load';
// Cron Tasks
$_['entry_cron_task_name'] = 'Task Name'; $_['entry_cron_task_name'] = 'Task Name';
$_['entry_cron_task_summary'] = 'Compression settings'; $_['entry_cron_task_summary'] = 'Compression settings';
$_['button_add_cron_task'] = 'Add Cron Task'; $_['button_add_cron_task'] = 'Add Cron Task';
$_['button_copy_cron_url'] = 'Copy URL'; $_['button_copy_cron_url'] = 'Copy URL';
$_['text_cron_recommended_url'] = 'Cron task execution URL:'; $_['text_cron_recommended_url'] = 'Cron task execution URL:';
$_['button_online_instruction'] = 'Module Documentation (Online)';
// 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';

View File

@ -0,0 +1,610 @@
<?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'] = 'Создание интерактивных промо-панелей и слайд-шоу в шапке сайта с конструктором блоков, таймерами, формой подписки и гибким таргетингом.';
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
$_['help_webp_on_fly'] = 'Молниеносная замена: автоматически меняет расширения .jpg/.png на .webp прямо в HTML-выводе витрины и добавляет атрибут loading="lazy". Не затрагивает базу данных!';
$_['entry_auto_fake_jpg_guard'] = 'Защита от фейковых JPG (On-the-Fly Guard)';
$_['help_auto_fake_jpg_guard'] = 'Автоматически выявляет при загрузке и импорте (1С, парсеры, CSV, FileManager) файлы PNG в маске .jpg, вызывающие чёрный фон на витрине, и исправляет их на лету при первом обращении.';
$_['entry_fake_jpg_mode'] = 'Режим исправления фейковых JPG:';
$_['text_fake_jpg_mode_jpg'] = 'Конвертировать в чистый JPG с белым фоном (#FFFFFF) [Рекомендуется]';
$_['text_fake_jpg_mode_png'] = 'Сохранить как настоящий PNG (прозрачность + обновление БД)';
$_['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_svg_autofix'] = 'Автофикс CSS-стилей SVG';
$_['help_support_svg_autofix'] = 'Применяет изолированные CSS-стили размеров для каталожных SVG-товаров без влияния на флаги языков и системные иконки.';
$_['entry_support_webp'] = 'Поддержка загрузки WebP (+ TrueFileManager)';
$_['help_support_webp'] = 'Позволяет выбирать, загружать и просматривать WebP через стандартный FileManager и TrueFileManager (elFinder).';
$_['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'] = 'Автообновление при входе';
$_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
$_['entry_cron_batch_limit'] = 'Лимит пакета за один запуск Крона (файлов)';
$_['help_cron_batch_limit'] = 'Максимальное количество файлов, обрабатываемых за один запуск Крона. По умолчанию 100. Рекомендуется от 50 до 300 для предотвращения таймаута выполнения на хостинге.';
$_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
$_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
$_['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'] = 'Настройка лимитов и сканирования диска';
$_['text_tool_svg_security'] = 'SVG Security Sanitizer (Защита XSS/XXE)';
$_['help_tool_svg_security'] = 'Сканирует SVG-векторные изображения в image/catalog/ на наличие опасного кода (скрипты, обработчики событий, XXE entity, foreignObject, javascript: URI) и очищает их.';
$_['text_description_svg_security'] = 'SVG Security Sanitizer проверяет все SVG-файлы в каталоге на уязвимости безопасности. Очистка удаляет вредоносные скрипты и инъекции, сохраняя графическое изображение.';
$_['text_svg_threats_found'] = 'Найдено угроз безопасности: %d';
$_['text_svg_threat_xxe_entity'] = 'XXE Entity атака';
$_['text_svg_threat_script_tag'] = 'Тег скрипта (<script>)';
$_['text_svg_threat_event_handler'] = 'Обработчик событий (on*)';
$_['text_svg_threat_javascript_uri']= 'JavaScript URI (javascript:)';
$_['text_svg_threat_foreign_object']= 'Чужеродный объект (<foreignObject>)';
$_['text_svg_threat_embed_object'] = 'Вложенный объект (<object>/<iframe>)';
$_['text_svg_threat_css_injection'] = 'CSS-инъекция';
$_['text_svg_clean_success'] = 'Очищено и безопасно';
$_['text_tool_black_bg'] = 'Поиск чёрного фона и Фейковых JPG';
$_['help_tool_black_bg'] = 'Находит PNG-изображения с расширением .jpg (из автоимпортов) и фото с чёрным фоном/рамками. Заменяет чёрный фон на белый (#FFFFFF).';
$_['text_description_black_bg'] = 'Инструмент ищет картинки товаров с чёрным фоном или некорректным расширением после импорта. Применение преобразует фейковые JPG и заменяет чёрные рамки вокруг товара белым фоном.';
$_['entry_bb_darkness'] = 'Порог темноты пикселя (0-100)';
$_['entry_bb_threshold'] = '% чёрных пикселей по краю';
$_['entry_bb_limit'] = 'Лимит сканирования (файлов)';
$_['entry_bb_scan_fake'] = 'Искать Fake JPG (PNG в .jpg)';
$_['entry_bb_scan_black'] = 'Искать чёрный фон';
$_['text_bb_fake_jpg'] = 'Фейковый JPG (PNG внутри .jpg)';
$_['text_bb_black_bg'] = 'Обнаружен чёрный фон';
$_['text_bb_fix_flatten_white'] = 'Конвертировать в JPG (белый фон)';
$_['text_bb_fix_convert_png'] = 'Сохранить как PNG (прозрачность)';
$_['text_bb_fix_white'] = 'Заменить чёрный фон (GD: только края)';
$_['text_bb_fix_white_all'] = 'Заменить ВСЕ чёрные пиксели (GD: агрессивно)';
$_['text_bb_fix_white_smooth'] = 'Заменить ВСЕ чёрные пиксели (GD: Defringe + сглаживание)';
$_['text_bb_fix_white_imagick'] = 'Заменить чёрный фон (Imagick: только края)';
$_['text_bb_fix_white_all_imagick'] = 'Заменить ВСЕ чёрные пиксели (Imagick: Fuzzy Lab)';
$_['text_bb_fix_white_smooth_imagick'] = 'Заменить ВСЕ чёрные пиксели (Imagick: Defringe + сглаживание)';
$_['text_bb_type_fake'] = 'Ошибка импорта PNG';
$_['text_bb_type_black'] = 'Чёрный фон';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Выделено диска на хостинг
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)'; $_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
$_['entry_auto_disk'] = 'Автообновление при входе'; $_['entry_auto_disk'] = 'Автообновление при входе';
// Cron Tasks
$_['entry_cron_task_name'] = 'Название задачи'; $_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия'; $_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу'; $_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL'; $_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:'; $_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
// Licensing
$_['text_license_required'] = 'Требуется лицензионный ключ'; $_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.'; $_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.'; $_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка'; $_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.'; $_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).'; $_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
@ -563,3 +560,7 @@ $_['entry_disk_status'] = 'Включить сканирование диска
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.'; $_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска'; $_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -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'] = 'Расширенный лог (показать используемые фото и где они найдены)';
$_['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'] = 'Автообновление при входе';
$_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
$_['entry_cron_batch_limit'] = 'Лимит пакета за один запуск Крона (файлов)';
$_['help_cron_batch_limit'] = 'Максимальное количество файлов, обрабатываемых за один запуск Крона. По умолчанию 100. Рекомендуется от 50 до 300 для предотвращения таймаута выполнения на хостинге.';
$_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
$_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
$_['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'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Выделено диска на хостинг
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)'; $_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
$_['entry_auto_disk'] = 'Автообновление при входе'; $_['entry_auto_disk'] = 'Автообновление при входе';
// Cron Tasks
$_['entry_cron_task_name'] = 'Название задачи'; $_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия'; $_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу'; $_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL'; $_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:'; $_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
// Licensing
$_['text_license_required'] = 'Требуется лицензионный ключ'; $_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.'; $_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.'; $_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка'; $_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.'; $_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).'; $_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
@ -563,3 +560,7 @@ $_['entry_disk_status'] = 'Включить сканирование диска
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.'; $_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска'; $_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -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'] = 'Расширенный лог (показать используемые фото и где они найдены)';
$_['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'] = 'Автообновление при входе';
$_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
$_['entry_cron_batch_limit'] = 'Лимит пакета за один запуск Крона (файлов)';
$_['help_cron_batch_limit'] = 'Максимальное количество файлов, обрабатываемых за один запуск Крона. По умолчанию 100. Рекомендуется от 50 до 300 для предотвращения таймаута выполнения на хостинге.';
$_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
$_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
$_['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'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Выделено диска на хостинг
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)'; $_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
$_['entry_auto_disk'] = 'Автообновление при входе'; $_['entry_auto_disk'] = 'Автообновление при входе';
// Cron Tasks
$_['entry_cron_task_name'] = 'Название задачи'; $_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия'; $_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу'; $_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL'; $_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:'; $_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
// Licensing
$_['text_license_required'] = 'Требуется лицензионный ключ'; $_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.'; $_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.'; $_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка'; $_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.'; $_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).'; $_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
@ -563,3 +560,7 @@ $_['entry_disk_status'] = 'Включить сканирование диска
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.'; $_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска'; $_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -0,0 +1,610 @@
<?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'] = 'Создание интерактивных промо-панелей и слайд-шоу в шапке сайта';
$_['entry_webp_on_fly'] = 'On-the-Fly WebP + Lazy Load';
$_['help_webp_on_fly'] = 'Молниеносная замена: автоматически меняет расширения .jpg/.png на .webp прямо в HTML-выводе витрины и добавляет атрибут loading="lazy". Не затрагивает базу данных!';
$_['entry_auto_fake_jpg_guard'] = 'Защита от фейковых JPG (On-the-Fly Guard)';
$_['help_auto_fake_jpg_guard'] = 'Автоматически выявляет при загрузке и импорте (1С, парсеры, CSV, FileManager) файлы PNG в маске .jpg, вызывающие чёрный фон на витрине, и исправляет их на лету при первом обращении.';
$_['entry_fake_jpg_mode'] = 'Режим исправления фейковых JPG:';
$_['text_fake_jpg_mode_jpg'] = 'Конвертировать в чистый JPG с белым фоном (#FFFFFF) [Рекомендуется]';
$_['text_fake_jpg_mode_png'] = 'Сохранить как настоящий PNG (прозрачность + обновление БД)';
$_['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_svg_autofix'] = 'Автофикс CSS-стилей SVG';
$_['help_support_svg_autofix'] = 'Применяет изолированные CSS-стили размеров для каталожных SVG-товаров без влияния на флаги языков и системные иконки.';
$_['entry_support_webp'] = 'Поддержка загрузки WebP (+ TrueFileManager)';
$_['help_support_webp'] = 'Позволяет выбирать, загружать и просматривать WebP через стандартный FileManager и TrueFileManager (elFinder).';
$_['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'] = 'Автообновление при входе';
$_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
$_['entry_cron_batch_limit'] = 'Лимит пакета за один запуск Крона (файлов)';
$_['help_cron_batch_limit'] = 'Максимальное количество файлов, обрабатываемых за один запуск Крона. По умолчанию 100. Рекомендуется от 50 до 300 для предотвращения таймаута выполнения на хостинге.';
$_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
$_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
$_['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'] = 'Настройка лимитов и сканирования диска';
$_['text_tool_svg_security'] = 'SVG Security Sanitizer (Защита XSS/XXE)';
$_['help_tool_svg_security'] = 'Сканирует SVG-векторные изображения в image/catalog/ на наличие опасного кода (скрипты, обработчики событий, XXE entity, foreignObject, javascript: URI) и очищает их.';
$_['text_description_svg_security'] = 'SVG Security Sanitizer проверяет все SVG-файлы в каталоге на уязвимости безопасности. Очистка удаляет вредоносные скрипты и инъекции, сохраняя графическое изображение.';
$_['text_svg_threats_found'] = 'Найдено угроз безопасности: %d';
$_['text_svg_threat_xxe_entity'] = 'XXE Entity атака';
$_['text_svg_threat_script_tag'] = 'Тег скрипта (<script>)';
$_['text_svg_threat_event_handler'] = 'Обработчик событий (on*)';
$_['text_svg_threat_javascript_uri']= 'JavaScript URI (javascript:)';
$_['text_svg_threat_foreign_object']= 'Чужеродный объект (<foreignObject>)';
$_['text_svg_threat_embed_object'] = 'Вложенный объект (<object>/<iframe>)';
$_['text_svg_threat_css_injection'] = 'CSS-инъекция';
$_['text_svg_clean_success'] = 'Очищено и безопасно';
$_['text_tool_black_bg'] = 'Поиск чёрного фона и Фейковых JPG';
$_['help_tool_black_bg'] = 'Находит PNG-изображения с расширением .jpg (из автоимпортов) и фото с чёрным фоном/рамками. Заменяет чёрный фон на белый (#FFFFFF).';
$_['text_description_black_bg'] = 'Инструмент ищет картинки товаров с чёрным фоном или некорректным расширением после импорта. Применение преобразует фейковые JPG и заменяет чёрные рамки вокруг товара белым фоном.';
$_['entry_bb_darkness'] = 'Порог темноты пикселя (0-100)';
$_['entry_bb_threshold'] = '% чёрных пикселей по краю';
$_['entry_bb_limit'] = 'Лимит сканирования (файлов)';
$_['entry_bb_scan_fake'] = 'Искать Fake JPG (PNG в .jpg)';
$_['entry_bb_scan_black'] = 'Искать чёрный фон';
$_['text_bb_fake_jpg'] = 'Фейковый JPG (PNG внутри .jpg)';
$_['text_bb_black_bg'] = 'Обнаружен чёрный фон';
$_['text_bb_fix_flatten_white'] = 'Конвертировать в JPG (белый фон)';
$_['text_bb_fix_convert_png'] = 'Сохранить как PNG (прозрачность)';
$_['text_bb_fix_white'] = 'Заменить чёрный фон (GD: только края)';
$_['text_bb_fix_white_all'] = 'Заменить ВСЕ чёрные пиксели (GD: агрессивно)';
$_['text_bb_fix_white_smooth'] = 'Заменить ВСЕ чёрные пиксели (GD: Defringe + сглаживание)';
$_['text_bb_fix_white_imagick'] = 'Заменить чёрный фон (Imagick: только края)';
$_['text_bb_fix_white_all_imagick'] = 'Заменить ВСЕ чёрные пиксели (Imagick: Fuzzy Lab)';
$_['text_bb_fix_white_smooth_imagick'] = 'Заменить ВСЕ чёрные пиксели (Imagick: Defringe + сглаживание)';
$_['text_bb_type_fake'] = 'Ошибка импорта PNG';
$_['text_bb_type_black'] = 'Чёрный фон';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Выделено диска на хостинг
$_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)'; $_['entry_disk_quota_used'] = 'Занято диска на хостинге всего (ГБ)';
$_['entry_auto_disk'] = 'Автообновление при входе'; $_['entry_auto_disk'] = 'Автообновление при входе';
// Cron Tasks
$_['entry_cron_task_name'] = 'Название задачи'; $_['entry_cron_task_name'] = 'Название задачи';
$_['entry_cron_task_summary'] = 'Параметры сжатия'; $_['entry_cron_task_summary'] = 'Параметры сжатия';
$_['button_add_cron_task'] = 'Добавить задачу'; $_['button_add_cron_task'] = 'Добавить задачу';
$_['button_copy_cron_url'] = 'Копировать URL'; $_['button_copy_cron_url'] = 'Копировать URL';
$_['text_cron_recommended_url'] = 'URL для запуска задачи крона:'; $_['text_cron_recommended_url'] = 'URL для запуска задачи крона:';
// Licensing
$_['text_license_required'] = 'Требуется лицензионный ключ'; $_['text_license_required'] = 'Требуется лицензионный ключ';
$_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.'; $_['text_license_required_desc'] = 'Пожалуйста, введите лицензионный ключ для активации модуля. Все функции настроек и оптимизации заблокированы до ввода валидного ключа.';
$_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.'; $_['entry_token_desc'] = 'Введите токен активации, выданный для вашего основного домена. Вы можете получить его в личном кабинете на форуме или обратившись в поддержку.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Активировать модуль';
$_['text_support'] = 'Техническая поддержка'; $_['text_support'] = 'Техническая поддержка';
$_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.'; $_['text_support_desc'] = 'Если у вас ещё нет ключа или возникли проблемы, пожалуйста, сообщите номер вашего заказа и домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. высота водяного знака (px)';
$_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).'; $_['help_wm_max_width'] = 'Если исходное изображение водяного знака превышает эту ширину, оно будет автоматически масштабировано. Укажите 0 или оставьте пустым для значения по умолчанию (800px).';
@ -562,3 +559,7 @@ $_['help_wm_max_height'] = 'Если исходное изображение в
$_['entry_disk_status'] = 'Включить сканирование диска и график'; $_['entry_disk_status'] = 'Включить сканирование диска и график';
$_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.'; $_['help_disk_status'] = 'Если включено, модуль будет периодически сканировать объем папки картинок и отображать круговую диаграмму. Рекомендуется выключить на больших сайтах для ускорения загрузки панели.';
$_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска'; $_['text_disk_settings_title'] = 'Настройка лимитов и сканирования диска';
$_['button_online_instruction'] = 'Инструкция к модулю (Онлайн)';

View File

@ -0,0 +1,569 @@
<?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'] = 'Автооновлення при вході';
$_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
$_['entry_cron_batch_limit'] = 'Ліміт пакета за один запуск Крона (файлів)';
$_['help_cron_batch_limit'] = 'Максимальна кількість файлів, що обробляються за один запуск Крона. За замовчуванням 100. Рекомендується від 50 до 300 для запобігання таймауту виконання на хостингу.';
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
$_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
$_['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'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -541,14 +541,12 @@ $_['entry_disk_quota_total'] = 'Виділено диска на хостинг
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)'; $_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
$_['entry_auto_disk'] = 'Автооновлення при вході'; $_['entry_auto_disk'] = 'Автооновлення при вході';
// Cron Tasks
$_['entry_cron_task_name'] = 'Назва задачі'; $_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення'; $_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу'; $_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL'; $_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:'; $_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
// Licensing
$_['text_license_required'] = 'Необхідний ліцензійний ключ'; $_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.'; $_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.'; $_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
@ -556,7 +554,6 @@ $_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка'; $_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.'; $_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).'; $_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
@ -565,3 +562,9 @@ $_['entry_disk_status'] = 'Увімкнути сканування диска т
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.'; $_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска'; $_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -0,0 +1,610 @@
<?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_svg_autofix'] = 'Автофікс CSS-стилів SVG';
$_['help_support_svg_autofix'] = 'Застосовує ізольовані CSS-стилі розмірів для каталожних SVG-товарів без впливу на прапорці мов та системні іконки.';
$_['entry_support_webp'] = 'Підтримка завантаження WebP (+ TrueFileManager)';
$_['help_support_webp'] = 'Дозволяє вибирати, завантажувати та шукати WebP-файли через стандартний FileManager та TrueFileManager (elFinder).';
$_['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". Не зачіпає базу даних!';
$_['entry_auto_fake_jpg_guard'] = 'Захист від фейкових JPG (On-the-Fly Guard)';
$_['help_auto_fake_jpg_guard'] = 'Автоматично виявляє при завантаженні та імпорті (1С, парсери, CSV, FileManager) файли PNG у масці .jpg, що викликають чорний фон на вітрині, та виправляє їх на льоту при першому зверненні.';
$_['entry_fake_jpg_mode'] = 'Режим виправлення фейкових JPG:';
$_['text_fake_jpg_mode_jpg'] = 'Конвертувати у чистий JPG з білим фоном (#FFFFFF) [Рекомендовано]';
$_['text_fake_jpg_mode_png'] = 'Зберегти як справжній PNG (прозорість + оновлення БД)';
$_['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'] = 'Автооновлення при вході';
$_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
$_['entry_cron_batch_limit'] = 'Ліміт пакета за один запуск Крона (файлів)';
$_['help_cron_batch_limit'] = 'Максимальна кількість файлів, що обробляються за один запуск Крона. За замовчуванням 100. Рекомендується від 50 до 300 для запобігання таймауту виконання на хостингу.';
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
$_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
$_['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'] = 'Налаштування лімітів та сканування диска';
$_['text_tool_svg_security'] = 'SVG Security Sanitizer (Захист XSS/XXE)';
$_['help_tool_svg_security'] = 'Сканує SVG-векторні зображення в image/catalog/ на наявність небезпечного коду (скрипти, обробники подій, XXE entity, foreignObject, javascript: URI) та очищає їх.';
$_['text_description_svg_security'] = 'SVG Security Sanitizer перевіряє всі SVG-файли в каталозі на вразливості безпеки. Очищення видаляє шкідливі скрипти та ін\'єкції, зберігаючи графічне зображення.';
$_['text_svg_threats_found'] = 'Знайдено загрози безпеки: %d';
$_['text_svg_threat_xxe_entity'] = 'XXE Entity атака';
$_['text_svg_threat_script_tag'] = 'Тег скрипту (<script>)';
$_['text_svg_threat_event_handler'] = 'Обробник подій (on*)';
$_['text_svg_threat_javascript_uri']= 'JavaScript URI (javascript:)';
$_['text_svg_threat_foreign_object']= 'Чужорідний об\'єкт (<foreignObject>)';
$_['text_svg_threat_embed_object'] = 'Вкладений об\'єкт (<object>/<iframe>)';
$_['text_svg_threat_css_injection'] = 'CSS-ін\'єкція';
$_['text_svg_clean_success'] = 'Очищено та безпечно';
$_['text_tool_black_bg'] = 'Пошук чорного фону та Фейкових JPG';
$_['help_tool_black_bg'] = 'Знаходить PNG-зображення з розширенням .jpg (з автоімпортів) та фото з чорним фоном/рамками. Замінює чорний фон на білий (#FFFFFF).';
$_['text_description_black_bg'] = 'Інструмент шукає картинки товарів із чорним фоном чи некоректним розширенням після імпорту. Застосування перетворює фейкові JPG і замінює чорні рамки навколо товару білим фоном.';
$_['entry_bb_darkness'] = 'Поріг темряви пикселя (0-100)';
$_['entry_bb_threshold'] = '% чорних пікселів по краю';
$_['entry_bb_limit'] = 'Ліміт сканування (файлів)';
$_['entry_bb_scan_fake'] = 'Шукати Fake JPG (PNG у .jpg)';
$_['entry_bb_scan_black'] = 'Шукати чорний фон';
$_['text_bb_fake_jpg'] = 'Фейковий JPG (PNG всередині .jpg)';
$_['text_bb_black_bg'] = 'Виявлено чорний фон';
$_['text_bb_fix_flatten_white'] = 'Конвертувати у JPG (білий фон)';
$_['text_bb_fix_convert_png'] = 'Зберегти як PNG (прозорість)';
$_['text_bb_fix_white'] = 'Замінити чорний фон (GD: тільки краї)';
$_['text_bb_fix_white_all'] = 'Замінити ВСІ чорні пікселі (GD: агресивно)';
$_['text_bb_fix_white_smooth'] = 'Замінити ВСІ чорні пікселі (GD: Defringe + сгладжування)';
$_['text_bb_fix_white_imagick'] = 'Замінити чорний фон (Imagick: тільки краї)';
$_['text_bb_fix_white_all_imagick'] = 'Замінити ВСІ чорні пікселі (Imagick: Fuzzy Lab)';
$_['text_bb_fix_white_smooth_imagick'] = 'Замінити ВСІ чорні пікселі (Imagick: Defringe + сгладжування)';
$_['text_bb_type_fake'] = 'Помилка імпорту PNG';
$_['text_bb_type_black'] = 'Чорний фон';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -541,14 +541,12 @@ $_['entry_disk_quota_total'] = 'Виділено диска на хостинг
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)'; $_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
$_['entry_auto_disk'] = 'Автооновлення при вході'; $_['entry_auto_disk'] = 'Автооновлення при вході';
// Cron Tasks
$_['entry_cron_task_name'] = 'Назва задачі'; $_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення'; $_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу'; $_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL'; $_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:'; $_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
// Licensing
$_['text_license_required'] = 'Необхідний ліцензійний ключ'; $_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.'; $_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.'; $_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
@ -556,7 +554,6 @@ $_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка'; $_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.'; $_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).'; $_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
@ -565,3 +562,9 @@ $_['entry_disk_status'] = 'Увімкнути сканування диска т
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.'; $_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска'; $_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -0,0 +1,569 @@
<?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'] = 'Автооновлення при вході';
$_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
$_['entry_cron_batch_limit'] = 'Ліміт пакета за один запуск Крона (файлів)';
$_['help_cron_batch_limit'] = 'Максимальна кількість файлів, що обробляються за один запуск Крона. За замовчуванням 100. Рекомендується від 50 до 300 для запобігання таймауту виконання на хостингу.';
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
$_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
$_['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'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -541,14 +541,12 @@ $_['entry_disk_quota_total'] = 'Виділено диска на хостинг
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)'; $_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
$_['entry_auto_disk'] = 'Автооновлення при вході'; $_['entry_auto_disk'] = 'Автооновлення при вході';
// Cron Tasks
$_['entry_cron_task_name'] = 'Назва задачі'; $_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення'; $_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу'; $_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL'; $_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:'; $_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
// Licensing
$_['text_license_required'] = 'Необхідний ліцензійний ключ'; $_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.'; $_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.'; $_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
@ -556,7 +554,6 @@ $_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка'; $_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.'; $_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).'; $_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
@ -565,3 +562,9 @@ $_['entry_disk_status'] = 'Увімкнути сканування диска т
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.'; $_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска'; $_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -0,0 +1,569 @@
<?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'] = 'Автооновлення при вході';
$_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
$_['entry_cron_batch_limit'] = 'Ліміт пакета за один запуск Крона (файлів)';
$_['help_cron_batch_limit'] = 'Максимальна кількість файлів, що обробляються за один запуск Крона. За замовчуванням 100. Рекомендується від 50 до 300 для запобігання таймауту виконання на хостингу.';
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
$_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
$_['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'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -541,14 +541,12 @@ $_['entry_disk_quota_total'] = 'Виділено диска на хостинг
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)'; $_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
$_['entry_auto_disk'] = 'Автооновлення при вході'; $_['entry_auto_disk'] = 'Автооновлення при вході';
// Cron Tasks
$_['entry_cron_task_name'] = 'Назва задачі'; $_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення'; $_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу'; $_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL'; $_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:'; $_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
// Licensing
$_['text_license_required'] = 'Необхідний ліцензійний ключ'; $_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.'; $_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.'; $_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
@ -556,7 +554,6 @@ $_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка'; $_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.'; $_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).'; $_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
@ -565,3 +562,9 @@ $_['entry_disk_status'] = 'Увімкнути сканування диска т
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.'; $_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска'; $_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -0,0 +1,610 @@
<?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_svg_autofix'] = 'Автофікс CSS-стилів SVG';
$_['help_support_svg_autofix'] = 'Застосовує ізольовані CSS-стилі розмірів для каталожних SVG-товарів без впливу на прапорці мов та системні іконки.';
$_['entry_support_webp'] = 'Підтримка завантаження WebP (+ TrueFileManager)';
$_['help_support_webp'] = 'Дозволяє вибирати, завантажувати та шукати WebP-файли через стандартний FileManager та TrueFileManager (elFinder).';
$_['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". Не зачіпає базу даних!';
$_['entry_auto_fake_jpg_guard'] = 'Захист від фейкових JPG (On-the-Fly Guard)';
$_['help_auto_fake_jpg_guard'] = 'Автоматично виявляє при завантаженні та імпорті (1С, парсери, CSV, FileManager) файли PNG у масці .jpg, що викликають чорний фон на вітрині, та виправляє їх на льоту при першому зверненні.';
$_['entry_fake_jpg_mode'] = 'Режим виправлення фейкових JPG:';
$_['text_fake_jpg_mode_jpg'] = 'Конвертувати у чистий JPG з білим фоном (#FFFFFF) [Рекомендовано]';
$_['text_fake_jpg_mode_png'] = 'Зберегти як справжній PNG (прозорість + оновлення БД)';
$_['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'] = 'Автооновлення при вході';
$_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
$_['entry_cron_batch_limit'] = 'Ліміт пакета за один запуск Крона (файлів)';
$_['help_cron_batch_limit'] = 'Максимальна кількість файлів, що обробляються за один запуск Крона. За замовчуванням 100. Рекомендується від 50 до 300 для запобігання таймауту виконання на хостингу.';
$_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форумі або звернувшись у підтримку.';
$_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
$_['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'] = 'Налаштування лімітів та сканування диска';
$_['text_tool_svg_security'] = 'SVG Security Sanitizer (Захист XSS/XXE)';
$_['help_tool_svg_security'] = 'Сканує SVG-векторні зображення в image/catalog/ на наявність небезпечного коду (скрипти, обробники подій, XXE entity, foreignObject, javascript: URI) та очищає їх.';
$_['text_description_svg_security'] = 'SVG Security Sanitizer перевіряє всі SVG-файли в каталозі на вразливості безпеки. Очищення видаляє шкідливі скрипти та ін\'єкції, зберігаючи графічне зображення.';
$_['text_svg_threats_found'] = 'Знайдено загрози безпеки: %d';
$_['text_svg_threat_xxe_entity'] = 'XXE Entity атака';
$_['text_svg_threat_script_tag'] = 'Тег скрипту (<script>)';
$_['text_svg_threat_event_handler'] = 'Обробник подій (on*)';
$_['text_svg_threat_javascript_uri']= 'JavaScript URI (javascript:)';
$_['text_svg_threat_foreign_object']= 'Чужорідний об\'єкт (<foreignObject>)';
$_['text_svg_threat_embed_object'] = 'Вкладений об\'єкт (<object>/<iframe>)';
$_['text_svg_threat_css_injection'] = 'CSS-ін\'єкція';
$_['text_svg_clean_success'] = 'Очищено та безпечно';
$_['text_tool_black_bg'] = 'Пошук чорного фону та Фейкових JPG';
$_['help_tool_black_bg'] = 'Знаходить PNG-зображення з розширенням .jpg (з автоімпортів) та фото з чорним фоном/рамками. Замінює чорний фон на білий (#FFFFFF).';
$_['text_description_black_bg'] = 'Інструмент шукає картинки товарів із чорним фоном чи некоректним розширенням після імпорту. Застосування перетворює фейкові JPG і замінює чорні рамки навколо товару білим фоном.';
$_['entry_bb_darkness'] = 'Поріг темряви пикселя (0-100)';
$_['entry_bb_threshold'] = '% чорних пікселів по краю';
$_['entry_bb_limit'] = 'Ліміт сканування (файлів)';
$_['entry_bb_scan_fake'] = 'Шукати Fake JPG (PNG у .jpg)';
$_['entry_bb_scan_black'] = 'Шукати чорний фон';
$_['text_bb_fake_jpg'] = 'Фейковий JPG (PNG всередині .jpg)';
$_['text_bb_black_bg'] = 'Виявлено чорний фон';
$_['text_bb_fix_flatten_white'] = 'Конвертувати у JPG (білий фон)';
$_['text_bb_fix_convert_png'] = 'Зберегти як PNG (прозорість)';
$_['text_bb_fix_white'] = 'Замінити чорний фон (GD: тільки краї)';
$_['text_bb_fix_white_all'] = 'Замінити ВСІ чорні пікселі (GD: агресивно)';
$_['text_bb_fix_white_smooth'] = 'Замінити ВСІ чорні пікселі (GD: Defringe + сгладжування)';
$_['text_bb_fix_white_imagick'] = 'Замінити чорний фон (Imagick: тільки краї)';
$_['text_bb_fix_white_all_imagick'] = 'Замінити ВСІ чорні пікселі (Imagick: Fuzzy Lab)';
$_['text_bb_fix_white_smooth_imagick'] = 'Замінити ВСІ чорні пікселі (Imagick: Defringe + сгладжування)';
$_['text_bb_type_fake'] = 'Помилка імпорту PNG';
$_['text_bb_type_black'] = 'Чорний фон';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

View File

@ -539,14 +539,12 @@ $_['entry_disk_quota_total'] = 'Виділено диска на хостинг
$_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)'; $_['entry_disk_quota_used'] = 'Зайнято диска на хостингу всього (ГБ)';
$_['entry_auto_disk'] = 'Автооновлення при вході'; $_['entry_auto_disk'] = 'Автооновлення при вході';
// Cron Tasks
$_['entry_cron_task_name'] = 'Назва задачі'; $_['entry_cron_task_name'] = 'Назва задачі';
$_['entry_cron_task_summary'] = 'Параметри стиснення'; $_['entry_cron_task_summary'] = 'Параметри стиснення';
$_['button_add_cron_task'] = 'Додати задачу'; $_['button_add_cron_task'] = 'Додати задачу';
$_['button_copy_cron_url'] = 'Копіювати URL'; $_['button_copy_cron_url'] = 'Копіювати URL';
$_['text_cron_recommended_url'] = 'URL для запуску задачі крону:'; $_['text_cron_recommended_url'] = 'URL для запуску задачі крону:';
// Licensing
$_['text_license_required'] = 'Необхідний ліцензійний ключ'; $_['text_license_required'] = 'Необхідний ліцензійний ключ';
$_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.'; $_['text_license_required_desc'] = 'Будь ласка, введіть ліцензійний ключ для активації модуля. Усі функції налаштувань та оптимізації заблоковані до введення валідного ключа.';
$_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форуме або звернувшись у підтримку.'; $_['entry_token_desc'] = 'Введіть токен активації, виданий для вашого основного домену. Ви можете отримати його в особистому кабінеті на форуме або звернувшись у підтримку.';
@ -554,7 +552,6 @@ $_['button_activate'] = 'Активувати модуль';
$_['text_support'] = 'Технічна підтримка'; $_['text_support'] = 'Технічна підтримка';
$_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.'; $_['text_support_desc'] = 'Якщо у вас ще немає ключа або виникли проблеми, будь ласка, повідомте номер вашого замовлення та домен.';
// New settings
$_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)'; $_['entry_wm_max_width'] = 'Макс. ширина водяного знака (px)';
$_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)'; $_['entry_wm_max_height'] = 'Макс. висота водяного знака (px)';
$_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).'; $_['help_wm_max_width'] = 'Якщо вихідне зображення водяного знака перевищує цю ширину, воно буде автоматично масштабоване. Вкажіть 0 або залиште порожнім для значення за замовчуванням (800px).';
@ -562,3 +559,9 @@ $_['help_wm_max_height'] = 'Якщо вихідне зображення вод
$_['entry_disk_status'] = 'Увімкнути сканування диска та графік'; $_['entry_disk_status'] = 'Увімкнути сканування диска та графік';
$_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.'; $_['help_disk_status'] = 'Якщо увімкнено, модуль періодично скануватиме обсяг папки картинок та відображатиме круговую діаграму. Рекомендується вимкнути на великих сайтах для прискорення завантаження панелі.';
$_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска'; $_['text_disk_settings_title'] = 'Налаштування лімітів та сканування диска';
$_['button_online_instruction'] = 'Інструкція до модуля (Онлайн)';

File diff suppressed because it is too large Load Diff

View File

@ -36,37 +36,6 @@
<div class="panel-body"> <div class="panel-body">
<form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form-module"> <form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form-module">
<?php if (!$is_licensed) { ?>
<div style="max-width: 600px; margin: 30px auto;">
<div class="panel panel-danger" style="border-radius: 6px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<div class="panel-heading" style="background-color: #d9534f; color: white; padding: 15px; border-top-left-radius: 5px; border-top-right-radius: 5px;">
<h3 class="panel-title" style="color: white; font-size: 16px; font-weight: bold;"><i class="fa fa-key"></i> <?php echo $text_license_required; ?></h3>
</div>
<div class="panel-body" style="padding: 25px;">
<div class="alert alert-danger" style="margin-bottom: 20px; font-size: 14px;">
<i class="fa fa-exclamation-triangle"></i> <?php echo $text_license_required_desc; ?>
</div>
<div class="form-group" style="margin-bottom: 20px;">
<label style="font-weight: bold; font-size: 14px; margin-bottom: 8px;"><?php echo $entry_token; ?></label>
<div style="font-size: 12px; color: #666; margin-bottom: 8px;"><?php echo $entry_token_desc; ?></div>
<div class="input-group">
<input type="text" name="module_img_opti_token" id="module_token" value="<?php echo $module_img_opti_token; ?>" class="form-control" placeholder="XXXX-XXXX-XXXX-XXXX" style="font-size: 16px; height: 46px; letter-spacing: 1px;">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary" style="height: 46px; font-weight: bold; padding: 0 20px;"><i class="fa fa-refresh"></i> <?php echo $button_activate; ?></button>
</span>
</div>
</div>
<hr>
<h4 style="font-weight: bold; margin-bottom: 10px;"><i class="fa fa-envelope-o text-primary"></i> <?php echo $text_support; ?></h4>
<p style="font-size: 13px; color: #555; line-height: 1.5;"><?php echo $text_support_desc; ?></p>
<ul style="font-size: 13px; color: #555; padding-left: 20px; line-height: 1.8;">
<li>Email: <b>info@nat.od.ua</b></li>
<li>Gitea: <b>https://git.nat.od.ua</b></li>
</ul>
</div>
</div>
</div>
<?php } else { ?>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="#tab-settings" data-toggle="tab"><i class="fa fa-cog"></i> <?php echo $tab_settings; ?></a></li> <li class="active"><a href="#tab-settings" data-toggle="tab"><i class="fa fa-cog"></i> <?php echo $tab_settings; ?></a></li>
<li><a href="#tab-optimize" data-toggle="tab"><i class="fa fa-compress"></i> <?php echo $tab_optimize; ?></a></li> <li><a href="#tab-optimize" data-toggle="tab"><i class="fa fa-compress"></i> <?php echo $tab_optimize; ?></a></li>
@ -126,51 +95,24 @@
</div> </div>
</div> </div>
<div class="panel-group" id="accordion-disk-settings" style="margin-top: 20px;"> <fieldset style="margin-top: 20px; border: 1px solid #ddd; padding: 15px; border-radius: 4px; background: #fff;">
<div class="panel panel-default" style="border: 1px solid #ddd; border-radius: 4px;"> <legend style="width: auto; padding: 0 10px; font-size: 16px; border: none; font-weight: bold; color: #2980b9;"><?php echo $text_disk_quota_legend; ?></legend>
<div class="panel-heading" style="background-color: #f5f5f5; padding: 10px 15px;"> <div class="alert alert-info" style="margin-bottom: 15px;"><?php echo $text_disk_quota_desc; ?></div>
<h4 class="panel-title"> <div class="row">
<a data-toggle="collapse" parent="#accordion-disk-settings" href="#collapse-disk-settings" style="display: block; text-decoration: none; font-weight: bold; color: #2980b9;"> <div class="col-md-6">
<i class="fa fa-chevron-down"></i> <?php echo $text_disk_settings_title; ?> <div class="form-group">
</a> <label class="control-label"><?php echo $entry_disk_quota_total; ?></label>
</h4> <input type="number" name="module_img_opti_disk_quota_total" value="<?php echo $module_img_opti_disk_quota_total; ?>" class="form-control" placeholder="20" step="0.1" min="0" />
</div>
</div> </div>
<div id="collapse-disk-settings" class="panel-collapse collapse"> <div class="col-md-6">
<div class="panel-body" style="background: #fff; padding: 20px;"> <div class="form-group">
<div class="row"> <label class="control-label"><?php echo $entry_disk_quota_used; ?></label>
<div class="col-sm-12"> <input type="number" name="module_img_opti_disk_quota_used" value="<?php echo $module_img_opti_disk_quota_used; ?>" class="form-control" placeholder="7.1" step="0.01" min="0" />
<div class="form-group" style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #dee2e6; margin-bottom: 20px;">
<label style="font-weight: bold; cursor: pointer; margin-bottom: 0;">
<input type="checkbox" name="module_img_opti_disk_status" value="1" <?php if (isset($module_img_opti_disk_status) && $module_img_opti_disk_status) { ?>checked="checked"<?php } ?> style="vertical-align: middle; margin-top: -2px; margin-right: 6px;" />
<?php echo $entry_disk_status; ?>
</label>
<div style="font-size: 11px; color: #888; margin-top: 5px;"><?php echo $help_disk_status; ?></div>
</div>
</div>
</div>
<fieldset style="border: 1px solid #eee; padding: 15px; border-radius: 4px; background: #fafafa;">
<legend style="width: auto; padding: 0 10px; font-size: 14px; border: none; font-weight: bold; color: #7f8c8d; margin-bottom: 10px;"><?php echo $text_disk_quota_legend; ?></legend>
<div class="alert alert-info" style="margin-bottom: 15px; font-size: 12px;"><?php echo $text_disk_quota_desc; ?></div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="control-label"><?php echo $entry_disk_quota_total; ?></label>
<input type="number" name="module_img_opti_disk_quota_total" value="<?php echo $module_img_opti_disk_quota_total; ?>" class="form-control" placeholder="20" step="0.1" min="0" />
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label"><?php echo $entry_disk_quota_used; ?></label>
<input type="number" name="module_img_opti_disk_quota_used" value="<?php echo $module_img_opti_disk_quota_used; ?>" class="form-control" placeholder="7.1" step="0.01" min="0" />
</div>
</div>
</div>
</fieldset>
</div> </div>
</div> </div>
</div> </div>
</div> </fieldset>
<fieldset style="margin-top: 20px; border: 1px solid #ddd; padding: 15px; border-radius: 4px; background: #fff;"> <fieldset style="margin-top: 20px; border: 1px solid #ddd; padding: 15px; border-radius: 4px; background: #fff;">
<legend style="width: auto; padding: 0 10px; font-size: 16px; border: none; font-weight: bold; color: #2980b9;"><?php echo $text_folder_rules_title; ?></legend> <legend style="width: auto; padding: 0 10px; font-size: 16px; border: none; font-weight: bold; color: #2980b9;"><?php echo $text_folder_rules_title; ?></legend>
@ -264,22 +206,25 @@
<button type="button" id="btn-reset-stats" class="btn btn-xs btn-danger pull-right" style="margin-top:-2px;" title="<?php echo $text_reset_stats; ?>"><i class="fa fa-trash"></i></button> <button type="button" id="btn-reset-stats" class="btn btn-xs btn-danger pull-right" style="margin-top:-2px;" title="<?php echo $text_reset_stats; ?>"><i class="fa fa-trash"></i></button>
</div> </div>
<div class="panel-body" style="padding:15px;"> <div class="panel-body" style="padding:15px;">
<div class="row" style="text-align:center;"> <div class="row" style="text-align:center;">
<div class="col-xs-4"> <div class="col-xs-4">
<div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_total_files_opt; ?></div> <div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_total_files_opt; ?></div>
<div id="cs-files" style="font-size:26px; font-weight:bold; color:#2c3e50;">&mdash;</div> <div id="cs-files" style="font-size:26px; font-weight:bold; color:#2c3e50;"><?php echo isset($stats_files) ? $stats_files : '0'; ?></div>
</div> </div>
<div class="col-xs-4"> <div class="col-xs-4">
<div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_total_saved; ?></div> <div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_total_saved; ?></div>
<div id="cs-saved" style="font-size:26px; font-weight:bold; color:#27ae60;">&mdash;</div> <div id="cs-saved" style="font-size:26px; font-weight:bold; color:#27ae60;"><?php echo isset($stats_saved_mb) ? $stats_saved_mb : '0.00'; ?> MB</div>
</div> </div>
<div class="col-xs-4"> <div class="col-xs-4">
<div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_avg_saving_pct; ?></div> <div style="font-size:11px; color:#666; text-transform:uppercase; letter-spacing:1px;"><?php echo $text_avg_saving_pct; ?></div>
<div id="cs-pct" style="font-size:26px; font-weight:bold; color:#e67e22;">&mdash;</div> <div id="cs-pct" style="font-size:26px; font-weight:bold; color:#e67e22;"><?php echo isset($stats_avg_pct) ? $stats_avg_pct : '0.0'; ?>%</div>
</div> </div>
</div> </div>
<hr style="margin:12px 0;"> <hr style="margin:12px 0;">
<div style="font-size:12px; color:#777; text-align:center;"><?php echo $text_stats_hint; ?></div> <div style="font-size:12px; color:#777; text-align:center; display: flex; justify-content: space-between; align-items: center;">
<span><i class="fa fa-database"></i> <?php echo $text_stats_hint; ?></span>
<button type="button" id="btn-reset-stats" class="btn btn-default btn-xs" style="color:#c0392b; border-color:#e74c3c;" title="<?php echo $text_reset_stats; ?>"><i class="fa fa-trash-o"></i> <?php echo $text_reset_stats; ?></button>
</div>
</div> </div>
</div> </div>
<div class="panel panel-default" style="border-color:#8e44ad;"> <div class="panel panel-default" style="border-color:#8e44ad;">
@ -288,7 +233,7 @@
</div> </div>
<div class="panel-body" style="display:flex; align-items:flex-start; gap:20px; flex-wrap:wrap;"> <div class="panel-body" style="display:flex; align-items:flex-start; gap:20px; flex-wrap:wrap;">
<div> <div>
<a href="index.php?route=module/img_opti/export_settings&token=<?php echo $token; ?>" class="btn btn-primary"> <a href="index.php?route=extension/module/img_opti/export_settings&token=<?php echo $token; ?>" class="btn btn-primary">
<i class="fa fa-download"></i> <?php echo $button_export_settings; ?> <i class="fa fa-download"></i> <?php echo $button_export_settings; ?>
</a> </a>
<div style="font-size:11px; color:#888; margin-top:5px;"><?php echo $help_export_settings; ?></div> <div style="font-size:11px; color:#888; margin-top:5px;"><?php echo $help_export_settings; ?></div>
@ -444,17 +389,17 @@
</div> </div>
</div> </div>
<div class="row" style="display: flex; flex-wrap: wrap; margin-left: -5px; margin-right: -5px;"> <div class="row">
<div class="col-sm-10 col-xs-9" id="block-start" style="padding-left: 5px; padding-right: 5px;"> <div class="col-sm-10" id="block-start">
<button type="button" id="btn-start" class="btn btn-primary btn-lg btn-block" <?php echo !$gd_enabled ? 'disabled' : ''; ?>><i class="fa fa-play"></i> <?php echo $button_start; ?></button> <button type="button" id="btn-start" class="btn btn-primary btn-lg btn-block" <?php echo !$gd_enabled ? 'disabled' : ''; ?>><i class="fa fa-play"></i> <?php echo $button_start; ?></button>
</div> </div>
<div class="col-sm-2" id="block-reset-cache" style="padding-left: 5px; padding-right: 5px;"> <div class="col-sm-2" id="block-reset-cache">
<button type="button" id="btn-reset-cache" class="btn btn-default btn-lg btn-block" title="<?php echo $text_reset_cache_hint; ?>"><i class="fa fa-eraser"></i></button> <button type="button" id="btn-reset-cache" class="btn btn-default btn-lg btn-block" title="<?php echo $text_reset_cache_hint; ?>"><i class="fa fa-eraser"></i></button>
</div> </div>
<div class="col-sm-6 col-xs-6" id="block-pause" style="display:none; padding-left: 5px; padding-right: 5px;"> <div class="col-sm-6" id="block-pause" style="display:none;">
<button type="button" id="btn-pause" class="btn btn-warning btn-lg btn-block"><i class="fa fa-pause"></i> <?php echo $button_pause; ?></button> <button type="button" id="btn-pause" class="btn btn-warning btn-lg btn-block"><i class="fa fa-pause"></i> <?php echo $button_pause; ?></button>
</div> </div>
<div class="col-sm-6" id="block-stop" style="display:none; padding-left: 5px; padding-right: 5px;"> <div class="col-sm-6" id="block-stop" style="display:none;">
<button type="button" id="btn-stop" class="btn btn-danger btn-lg btn-block"><i class="fa fa-stop"></i> <?php echo $button_stop; ?></button> <button type="button" id="btn-stop" class="btn btn-danger btn-lg btn-block"><i class="fa fa-stop"></i> <?php echo $button_stop; ?></button>
</div> </div>
</div> </div>
@ -613,10 +558,17 @@
<div class="col-md-3"> <div class="col-md-3">
<div class="checkbox" style="background: #fdfdfd; padding: 14px; border: 1px solid #eee; border-radius: 6px; min-height: 140px;"> <div class="checkbox" style="background: #fdfdfd; padding: 14px; border: 1px solid #eee; border-radius: 6px; min-height: 140px;">
<label style="font-weight: bold; font-size: 14px;"> <label style="font-weight: bold; font-size: 14px;">
<input type="checkbox" name="module_img_opti_support_svg" value="1" <?php if (isset($module_img_opti_support_svg) && $module_img_opti_support_svg) { ?>checked="checked"<?php } ?>> <input type="checkbox" name="module_img_opti_support_svg" id="chk-support-svg" value="1" <?php if (isset($module_img_opti_support_svg) && $module_img_opti_support_svg) { ?>checked="checked"<?php } ?>>
<?php echo $entry_support_svg; ?> <?php echo $entry_support_svg; ?>
</label> </label>
<div style="font-size: 12px; color: #666; margin-top: 8px; line-height: 1.4;"><?php echo $help_support_svg; ?></div> <div style="font-size: 12px; color: #666; margin-top: 8px; line-height: 1.4;"><?php echo $help_support_svg; ?></div>
<div id="svg-autofix-box" style="<?php if (empty($module_img_opti_support_svg)) { ?>display: none;<?php } ?> margin-top: 10px; padding-top: 8px; border-top: 1px dashed #ddd;">
<label style="font-size: 12px; font-weight: normal; color: #444;">
<input type="checkbox" name="module_img_opti_support_svg_autofix" value="1" <?php if (isset($module_img_opti_support_svg_autofix) && $module_img_opti_support_svg_autofix) { ?>checked="checked"<?php } ?>>
<b><?php echo $entry_support_svg_autofix; ?></b>
</label>
<div style="font-size: 11px; color: #777; margin-top: 2px;"><?php echo $help_support_svg_autofix; ?></div>
</div>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
@ -648,6 +600,29 @@
</div> </div>
</div> </div>
<div class="row" style="margin-top: 20px;">
<div class="col-md-12">
<div style="background: #fffdf5; padding: 16px; border: 1px solid #f39c12; border-radius: 6px;">
<div class="checkbox" style="margin-top: 0; margin-bottom: 8px;">
<label style="font-weight: bold; font-size: 14px; color: #d35400;">
<input type="checkbox" name="module_img_opti_auto_fake_jpg_guard" id="chk-auto-fake-jpg" value="1" <?php if (isset($module_img_opti_auto_fake_jpg_guard) && $module_img_opti_auto_fake_jpg_guard) { ?>checked="checked"<?php } ?>>
<i class="fa fa-shield"></i> <?php echo $entry_auto_fake_jpg_guard; ?>
</label>
</div>
<div style="font-size: 12px; color: #666; line-height: 1.4; margin-bottom: 10px;">
<?php echo $help_auto_fake_jpg_guard; ?>
</div>
<div id="fake-jpg-mode-box" style="<?php if (empty($module_img_opti_auto_fake_jpg_guard)) { ?>display: none;<?php } ?> margin-top: 10px; padding-top: 10px; border-top: 1px dashed #f0c36d; max-width: 600px;">
<label style="font-weight: bold; color: #333; font-size: 13px; margin-bottom: 5px;"><?php echo $entry_fake_jpg_mode; ?></label>
<select name="module_img_opti_fake_jpg_mode" class="form-control input-sm">
<option value="jpg" <?php if (empty($module_img_opti_fake_jpg_mode) || $module_img_opti_fake_jpg_mode == 'jpg') { ?>selected="selected"<?php } ?>><?php echo $text_fake_jpg_mode_jpg; ?></option>
<option value="png" <?php if (isset($module_img_opti_fake_jpg_mode) && $module_img_opti_fake_jpg_mode == 'png') { ?>selected="selected"<?php } ?>><?php echo $text_fake_jpg_mode_png; ?></option>
</select>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 20px;"> <div class="row" style="margin-top: 20px;">
<div class="col-md-4"> <div class="col-md-4">
<div class="checkbox" style="background: #fffdf5; padding: 15px; border: 1px solid #f39c12; border-radius: 6px; min-height: 150px; margin-top: 0; margin-bottom: 0;"> <div class="checkbox" style="background: #fffdf5; padding: 15px; border: 1px solid #f39c12; border-radius: 6px; min-height: 150px; margin-top: 0; margin-bottom: 0;">
@ -731,30 +706,18 @@
<div id="tool-ui-watermark" class="tool-ui-block" style="display: none;"> <div id="tool-ui-watermark" class="tool-ui-block" style="display: none;">
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-8">
<div class="form-group"> <div class="form-group">
<label class="control-label"><?php echo $entry_wm_status; ?></label> <label class="control-label"><?php echo $entry_wm_status; ?></label>
<select name="module_img_opti_wm_status" class="form-control"> <select name="module_img_opti_wm_status" class="form-control">
<option value="1" <?php if (isset($module_img_opti_wm_status) && $module_img_opti_wm_status) { ?>selected="selected"<?php } ?>><?php echo $text_enabled; ?></option> <option value="1" <?php if (isset($module_img_opti_wm_status) && $module_img_opti_wm_status) { ?>selected="selected"<?php } ?>><?php echo $text_enabled; ?></option>
<option value="0" <?php if (!isset($module_img_opti_wm_status) || !$module_img_opti_wm_status) { ?>selected="selected"<?php } ?>><?php echo $text_disabled; ?></option> <option value="0" <?php if (!isset($module_img_opti_wm_status) || !$module_img_opti_wm_status) { ?>selected="selected"<?php } ?>><?php echo $text_disabled; ?></option>
</select> </select>
</div> </div>
</div> </div>
<div class="col-sm-2"> <div class="col-sm-4" style="padding-top: 23px;">
<div class="form-group"> <button type="button" id="btn-clear-cache-wm" class="btn btn-warning btn-block" data-loading-text="<i class='fa fa-spinner fa-spin'></i> Cleaning..."><i class="fa fa-eraser"></i> <?php echo $button_clear_cache_wm; ?></button>
<label class="control-label" title="<?php echo $help_wm_max_width; ?>"><?php echo $entry_wm_max_width; ?></label> </div>
<input type="number" name="module_img_opti_wm_max_width" value="<?php echo $module_img_opti_wm_max_width; ?>" class="form-control" placeholder="800" min="0" />
</div>
</div>
<div class="col-sm-2">
<div class="form-group">
<label class="control-label" title="<?php echo $help_wm_max_height; ?>"><?php echo $entry_wm_max_height; ?></label>
<input type="number" name="module_img_opti_wm_max_height" value="<?php echo $module_img_opti_wm_max_height; ?>" class="form-control" placeholder="800" min="0" />
</div>
</div>
<div class="col-sm-4" style="padding-top: 23px;">
<button type="button" id="btn-clear-cache-wm" class="btn btn-warning btn-block" data-loading-text="<i class='fa fa-spinner fa-spin'></i> Cleaning..."><i class="fa fa-eraser"></i> <?php echo $button_clear_cache_wm; ?></button>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
@ -946,7 +909,7 @@
<div class="tab-pane" id="tab-cron"> <div class="tab-pane" id="tab-cron">
<div class="alert alert-info" style="background: #2c3e50; color: white; border: none;"> <div class="alert alert-info" style="background: #2c3e50; color: white; border: none;">
<i class="fa fa-clock-o"></i> <b><?php echo $text_cron_title; ?></b> <?php echo $text_cron_desc; ?><br> <i class="fa fa-clock-o"></i> <b><?php echo $text_cron_title; ?></b> <?php echo $text_cron_desc; ?><br>
<code style="display:inline-block; margin-top:5px; background:#fff; padding:5px 10px; color:#333;">wget -q -O - "<?php echo str_replace(\'image/catalog\', \'\', $catalog_url); ?>index.php?route=module/img_opti_cron&amp;key=<?php echo $module_img_opti_token; ?>" &gt;/dev/null 2&gt;&amp;1</code> <code style="display:inline-block; margin-top:5px; background:#fff; padding:5px 10px; color:#333;">wget -q -O - "<?php echo str_replace(\'image/catalog\', \'\', $catalog_url); ?>index.php?route=extension/module/img_opti_cron&amp;key=<?php echo $module_img_opti_token; ?>" &gt;/dev/null 2&gt;&amp;1</code>
</div> </div>
<div class="row"> <div class="row">
@ -992,6 +955,11 @@
<h3 style="margin-top: 0; color: #2c3e50;"><i class="fa fa-rocket"></i> <?php echo $text_promo_title; ?></h3> <h3 style="margin-top: 0; color: #2c3e50;"><i class="fa fa-rocket"></i> <?php echo $text_promo_title; ?></h3>
<p style="color: #333;"><?php echo $text_promo_desc; ?></p> <p style="color: #333;"><?php echo $text_promo_desc; ?></p>
<p style="color: #333;"><?php echo $text_support; ?> <a href="mailto:info@nat.od.ua">info@nat.od.ua</a></p> <p style="color: #333;"><?php echo $text_support; ?> <a href="mailto:info@nat.od.ua">info@nat.od.ua</a></p>
<div style="margin-top: 15px; margin-bottom: 5px;">
<a href="http://oc3-demo.nat.od.ua/instructions/image_optimizer_nat_instruction.html" target="_blank" class="btn btn-primary btn-lg" style="border-radius: 6px; font-weight: bold; background: linear-gradient(135deg, #2980b9, #3498db); border: none; box-shadow: 0 4px 12px rgba(41, 128, 185, 0.3);">
<i class="fa fa-book"></i> <?php echo $button_online_instruction; ?>
</a>
</div>
</div> </div>
<div class="row"> <div class="row">
@ -1074,7 +1042,6 @@
</div> </div>
</div> </div>
<?php } ?>
</form> </form>
</div> </div>
@ -1158,11 +1125,13 @@ function showToolUI(tool) {
} }
function restoreScanCache() { function restoreScanCache() {
var selectedTool = localStorage.getItem('nat_selected_tool_img_opti');
var cached = sessionStorage.getItem('nat_scan_cache_img_opti'); var cached = sessionStorage.getItem('nat_scan_cache_img_opti');
if (!cached) return; if (!cached) return;
try { try {
var data = JSON.parse(cached); var data = JSON.parse(cached);
if (data && data.rows && data.headers) { if (data && data.rows && data.headers) {
if (selectedTool && data.tool && selectedTool !== data.tool) return;
currentTool = data.tool || ''; currentTool = data.tool || '';
gridData = data.rows; gridData = data.rows;
gridHeaders = data.headers; gridHeaders = data.headers;
@ -1334,7 +1303,7 @@ $(document).on('change', '#grid-per-page', function() {
saveScanCache(); saveScanCache();
} else { } else {
var limit = $(this).val(); var limit = $(this).val();
window.location = 'index.php?route=module/img_opti&' + tokenParam + '&limit=' + limit; window.location = 'index.php?route=extension/module/img_opti&' + tokenParam + '&limit=' + limit;
} }
}); });
@ -1360,6 +1329,22 @@ $(document).ready(function() {
} }
}); });
$(document).on('change', '#chk-auto-fake-jpg', function() {
if ($(this).is(':checked')) {
$('#fake-jpg-mode-box').slideDown();
} else {
$('#fake-jpg-mode-box').slideUp();
}
});
$(document).on('change', '#chk-support-svg', function() {
if ($(this).is(':checked')) {
$('#svg-autofix-box').slideDown();
} else {
$('#svg-autofix-box').slideUp();
}
});
$('#wm_type').on('change', function() { $('#wm_type').on('change', function() {
if ($(this).val() == 'text') { if ($(this).val() == 'text') {
$('#wm_image_wrap').hide(); $('#wm_image_wrap').hide();
@ -1388,7 +1373,7 @@ $(document).ready(function() {
$('#btn-preview-wm').on('click', function() { $('#btn-preview-wm').on('click', function() {
var $btn = $(this).prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>'); var $btn = $(this).prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>');
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/preview_watermark&' + tokenParam, url: 'index.php?route=extension/module/img_opti/preview_watermark&' + tokenParam,
type: 'post', type: 'post',
data: $('#form-module').serialize(), data: $('#form-module').serialize(),
dataType: 'json', dataType: 'json',
@ -1793,7 +1778,7 @@ $(document).ready(function() {
module_img_opti_wm_corner_radius: $('#wm-rule-corner-radius').val() module_img_opti_wm_corner_radius: $('#wm-rule-corner-radius').val()
}; };
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/preview_watermark&' + tokenParam, url: 'index.php?route=extension/module/img_opti/preview_watermark&' + tokenParam,
type: 'post', type: 'post',
data: ruleData, data: ruleData,
dataType: 'json', dataType: 'json',
@ -1815,7 +1800,7 @@ $(document).ready(function() {
$('#input-modal-wm-category').autocomplete({ $('#input-modal-wm-category').autocomplete({
'source': function(request, response) { 'source': function(request, response) {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/autocomplete_category&' + tokenParam + '&filter_name=' + encodeURIComponent(request), url: 'index.php?route=extension/module/img_opti/autocomplete_category&' + tokenParam + '&filter_name=' + encodeURIComponent(request),
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
response($.map(json, function(item) { response($.map(json, function(item) {
@ -1837,7 +1822,7 @@ $(document).ready(function() {
$('#input-modal-wm-brand').autocomplete({ $('#input-modal-wm-brand').autocomplete({
'source': function(request, response) { 'source': function(request, response) {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/autocomplete_manufacturer&' + tokenParam + '&filter_name=' + encodeURIComponent(request), url: 'index.php?route=extension/module/img_opti/autocomplete_manufacturer&' + tokenParam + '&filter_name=' + encodeURIComponent(request),
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
response($.map(json, function(item) { response($.map(json, function(item) {
@ -1859,7 +1844,7 @@ $(document).ready(function() {
$('#input-modal-ph-category').autocomplete({ $('#input-modal-ph-category').autocomplete({
'source': function(request, response) { 'source': function(request, response) {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/autocomplete_category&' + tokenParam + '&filter_name=' + encodeURIComponent(request), url: 'index.php?route=extension/module/img_opti/autocomplete_category&' + tokenParam + '&filter_name=' + encodeURIComponent(request),
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
response($.map(json, function(item) { response($.map(json, function(item) {
@ -1881,7 +1866,7 @@ $(document).ready(function() {
$('#input-modal-ph-brand').autocomplete({ $('#input-modal-ph-brand').autocomplete({
'source': function(request, response) { 'source': function(request, response) {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/autocomplete_manufacturer&' + tokenParam + '&filter_name=' + encodeURIComponent(request), url: 'index.php?route=extension/module/img_opti/autocomplete_manufacturer&' + tokenParam + '&filter_name=' + encodeURIComponent(request),
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
response($.map(json, function(item) { response($.map(json, function(item) {
@ -1933,17 +1918,17 @@ $(document).ready(function() {
var statusText = task.status ? '<span class="label label-success"><?php echo $text_enabled; ?></span>' : '<span class="label label-danger"><?php echo $text_disabled; ?></span>'; var statusText = task.status ? '<span class="label label-success"><?php echo $text_enabled; ?></span>' : '<span class="label label-danger"><?php echo $text_disabled; ?></span>';
var escapedTaskName = encodeURIComponent(task.name); var escapedTaskName = encodeURIComponent(task.name);
var taskUrl = '<?php echo str_replace(\'image/catalog\', \'\', $catalog_url); ?>index.php?route=module/img_opti_cron&amp;key=<?php echo $module_img_opti_token; ?>&amp;task=' + escapedTaskName; var taskUrl = '<?php echo str_replace(\'image/catalog\', \'\', $catalog_url); ?>index.php?route=extension/module/img_opti_cron&amp;key=<?php echo $module_img_opti_token; ?>&amp;task=' + escapedTaskName;
html += '<tr>'; html += '<tr>';
html += ' <td><b>' + task.name + '</b><br><small class="text-muted">' + entitiesList + '</small></td>'; html += ' <td><b>' + task.name + '</b><br><small class="text-muted">' + entitiesList + '</small></td>';
html += ' <td><small>' + foldersList + '</small></td>'; html += ' <td><small>' + foldersList + '</small></td>';
html += ' <td><small>' + qualityInfo + '</small></td>'; html += ' <td><small>' + qualityInfo + '</small></td>';
html += ' <td class="text-center">' + statusText + '</td>'; html += ' <td class="text-center">' + statusText + '</td>';
html += ' <td class="text-right" style="white-space: normal; min-width: 250px;">'; html += ' <td class="text-right">';
html += ' <button type="button" class="btn btn-default btn-xs btn-copy-task-url" data-url="' + taskUrl.replace(/&amp;/g, '&') + '" title="<?php echo $button_copy_cron_url; ?>" style="margin: 2px 0 2px 5px;"><i class="fa fa-copy"></i> <?php echo $button_copy_cron_url; ?></button>'; html += ' <button type="button" class="btn btn-default btn-xs btn-copy-task-url" data-url="' + taskUrl.replace(/&amp;/g, '&') + '" title="<?php echo $button_copy_cron_url; ?>"><i class="fa fa-copy"></i> <?php echo $button_copy_cron_url; ?></button>';
html += ' <button type="button" class="btn btn-primary btn-xs btn-edit-cron-task" data-index="' + index + '" style="margin: 2px 0 2px 5px;"><i class="fa fa-pencil"></i> <?php echo $button_edit; ?></button>'; html += ' <button type="button" class="btn btn-primary btn-xs btn-edit-cron-task" data-index="' + index + '" style="margin-left:5px;"><i class="fa fa-pencil"></i> <?php echo $button_edit; ?></button>';
html += ' <button type="button" class="btn btn-danger btn-xs btn-delete-cron-task" data-index="' + index + '" style="margin: 2px 0 2px 5px;"><i class="fa fa-trash"></i> <?php echo $button_delete; ?></button>'; html += ' <button type="button" class="btn btn-danger btn-xs btn-delete-cron-task" data-index="' + index + '" style="margin-left:5px;"><i class="fa fa-trash"></i> <?php echo $button_delete; ?></button>';
html += ' </td>'; html += ' </td>';
html += '</tr>'; html += '</tr>';
}); });
@ -2143,6 +2128,9 @@ $(document).ready(function() {
$('#grid-container').slideUp(); $('#grid-container').slideUp();
gridData = []; gridData = [];
var tool = $(this).val(); var tool = $(this).val();
if (tool) {
localStorage.setItem('nat_selected_tool_img_opti', tool);
}
if (tool && toolDescriptions[tool]) { if (tool && toolDescriptions[tool]) {
$('#tool-help-text').html('<i class="fa fa-info-circle"></i> ' + toolDescriptions[tool]).slideDown(); $('#tool-help-text').html('<i class="fa fa-info-circle"></i> ' + toolDescriptions[tool]).slideDown();
@ -2179,7 +2167,7 @@ $(document).ready(function() {
$('.alert-dismissible').remove(); $('.alert-dismissible').remove();
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/run_tool&' + tokenParam, url: 'index.php?route=extension/module/img_opti/run_tool&' + tokenParam,
type: 'post', type: 'post',
data: { data: {
tool: 'watermark', tool: 'watermark',
@ -2226,7 +2214,7 @@ $(document).ready(function() {
formData += '&tool=' + tool + '&action=scan'; formData += '&tool=' + tool + '&action=scan';
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/run_tool&' + tokenParam, url: 'index.php?route=extension/module/img_opti/run_tool&' + tokenParam,
type: 'post', type: 'post',
data: formData, data: formData,
dataType: 'json', dataType: 'json',
@ -2292,7 +2280,7 @@ $(document).ready(function() {
} }
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/run_tool&' + tokenParam, url: 'index.php?route=extension/module/img_opti/run_tool&' + tokenParam,
type: 'post', type: 'post',
data: { data: {
tool: currentTool, tool: currentTool,
@ -2506,7 +2494,7 @@ $(document).on('click', '#btn-start', function(e) {
$('#form-opti input, #form-opti select, #input-threads').not('#btn-pause, #btn-stop').prop('disabled', true); $('#form-opti input, #form-opti select, #input-threads').not('#btn-pause, #btn-stop').prop('disabled', true);
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/scan&' + tokenParam, url: 'index.php?route=extension/module/img_opti/scan&' + tokenParam,
type: 'post', type: 'post',
data: optiParams, data: optiParams,
dataType: 'json', dataType: 'json',
@ -2570,7 +2558,7 @@ $(document).on('click', '#btn-stop', function(e) {
$('#btn-pause').prop('disabled', true); $('#btn-pause').prop('disabled', true);
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/stop&' + tokenParam, url: 'index.php?route=extension/module/img_opti/stop&' + tokenParam,
dataType: 'json', dataType: 'json',
success: function() { success: function() {
fullLogArray.push('<div class="text-danger" style="margin-top:10px; font-weight:bold; font-size:15px;"><?php echo $text_stopped_user; ?></div>'); fullLogArray.push('<div class="text-danger" style="margin-top:10px; font-weight:bold; font-size:15px;"><?php echo $text_stopped_user; ?></div>');
@ -2593,7 +2581,7 @@ $(document).on('click', '#btn-reset-cache', function() {
var $btn = $(this); var $btn = $(this);
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>'); $btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>');
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/resetCache&' + tokenParam, url: 'index.php?route=extension/module/img_opti/resetCache&' + tokenParam,
type: 'post', type: 'post',
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
@ -2614,7 +2602,7 @@ function processBatch() {
return; return;
} }
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/process&' + tokenParam, url: 'index.php?route=extension/module/img_opti/process&' + tokenParam,
type: 'post', type: 'post',
data: optiParams, data: optiParams,
dataType: 'json', dataType: 'json',
@ -2690,7 +2678,7 @@ $(document).on('click', '#btn-apply-changes', function() {
var backup = $('input[name="apply_backup"]:checked').val(); var backup = $('input[name="apply_backup"]:checked').val();
var folder = $('#copy_folder').val(); var folder = $('#copy_folder').val();
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/applyChanges&' + tokenParam, url: 'index.php?route=extension/module/img_opti/applyChanges&' + tokenParam,
type: 'post', type: 'post',
data: { backup: backup, copy_folder: folder }, data: { backup: backup, copy_folder: folder },
dataType: 'json', dataType: 'json',
@ -2718,7 +2706,7 @@ $(document).on('click', '#btn-full-cleanup', function() {
function clearSpecificTrash() { function clearSpecificTrash() {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/clearTrash&' + tokenParam, url: 'index.php?route=extension/module/img_opti/clearTrash&' + tokenParam,
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
if (json['success']) { if (json['success']) {
@ -2802,7 +2790,7 @@ $(document).on('click', '#btn-scan-trash', function(e) {
cleanerCurrentPage = 1; cleanerCurrentPage = 1;
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/build_index&' + tokenParam, url: 'index.php?route=extension/module/img_opti/build_index&' + tokenParam,
type: 'post', type: 'post',
data: { module_token: $('#module_token').val() }, data: { module_token: $('#module_token').val() },
dataType: 'json', dataType: 'json',
@ -2813,7 +2801,7 @@ $(document).on('click', '#btn-scan-trash', function(e) {
} }
$('#cleaner-status-text').html('<i class="fa fa-spinner fa-spin"></i> <?php echo $text_comparing; ?>'); $('#cleaner-status-text').html('<i class="fa fa-spinner fa-spin"></i> <?php echo $text_comparing; ?>');
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/scan_trash&' + tokenParam, url: 'index.php?route=extension/module/img_opti/scan_trash&' + tokenParam,
type: 'post', type: 'post',
data: $('#form-cleaner :input').serialize() + '&module_token=' + $('#module_token').val(), data: $('#form-cleaner :input').serialize() + '&module_token=' + $('#module_token').val(),
dataType: 'json', dataType: 'json',
@ -2858,7 +2846,7 @@ $(document).on('click', '#btn-do-quarantine, #btn-do-delete', function(e) {
function processTrashBatch() { function processTrashBatch() {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/process_trash&' + tokenParam, url: 'index.php?route=extension/module/img_opti/process_trash&' + tokenParam,
type: 'post', type: 'post',
data: { action: action, module_token: $('#module_token').val() }, data: { action: action, module_token: $('#module_token').val() },
dataType: 'json', dataType: 'json',
@ -2887,7 +2875,7 @@ $(document).on('click', '#btn-empty-quarantine', function(e) {
e.preventDefault(); e.preventDefault();
if (!confirm('<?php echo $confirm_empty_q; ?>')) return; if (!confirm('<?php echo $confirm_empty_q; ?>')) return;
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/empty_quarantine&' + tokenParam, url: 'index.php?route=extension/module/img_opti/empty_quarantine&' + tokenParam,
type: 'post', type: 'post',
data: { module_token: $('#module_token').val() }, data: { module_token: $('#module_token').val() },
dataType: 'json', dataType: 'json',
@ -2914,7 +2902,7 @@ $(document).on('click', '#btn-restore-quarantine', function(e) {
$('#cleaner-log-pagination').show(); $('#cleaner-log-pagination').show();
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/restore_quarantine&' + tokenParam, url: 'index.php?route=extension/module/img_opti/restore_quarantine&' + tokenParam,
type: 'post', type: 'post',
data: { module_token: $('#module_token').val() }, data: { module_token: $('#module_token').val() },
dataType: 'json', dataType: 'json',
@ -3036,7 +3024,7 @@ $(document).on('click', '.btn-compare-preview, .btn-compare-opti', function(e) {
if ($('input[name="webp_quality"]').length) postData.webp_quality = $('input[name="webp_quality"]').val(); if ($('input[name="webp_quality"]').length) postData.webp_quality = $('input[name="webp_quality"]').val();
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/compare_preview&' + tokenParam, url: 'index.php?route=extension/module/img_opti/compare_preview&' + tokenParam,
type: 'post', type: 'post',
data: postData, data: postData,
dataType: 'json', dataType: 'json',
@ -3070,12 +3058,12 @@ $(document).on('click', '#btn-download-copies', function(e) {
var folder = $('#copy_folder').val(); var folder = $('#copy_folder').val();
if (!folder) return; if (!folder) return;
window.location = 'index.php?route=module/img_opti/download_folder&' + tokenParam + '&folder=' + encodeURIComponent(folder); window.location = 'index.php?route=extension/module/img_opti/download_folder&' + tokenParam + '&folder=' + encodeURIComponent(folder);
setTimeout(function() { setTimeout(function() {
if (confirm('<?php echo $confirm_download_and_delete; ?>')) { if (confirm('<?php echo $confirm_download_and_delete; ?>')) {
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/clearTrash&' + tokenParam, url: 'index.php?route=extension/module/img_opti/clearTrash&' + tokenParam,
type: 'post', type: 'post',
data: { folder: folder }, data: { folder: folder },
dataType: 'json', dataType: 'json',
@ -3096,7 +3084,7 @@ $(document).on('click', '#btn-clear-backups', function(e) {
e.preventDefault(); e.preventDefault();
if (!confirm('<?php echo $confirm_clear_backups; ?>')) return; if (!confirm('<?php echo $confirm_clear_backups; ?>')) return;
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/clear_backups&' + tokenParam, url: 'index.php?route=extension/module/img_opti/clear_backups&' + tokenParam,
dataType: 'json', dataType: 'json',
success: function(json) { success: function(json) {
if (json['success']) { if (json['success']) {
@ -3163,7 +3151,7 @@ $('#btn-load-disk').on('click', function(e, triggerType) {
var isManual = (triggerType !== 'auto'); var isManual = (triggerType !== 'auto');
var btn = $(this).prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>'); var btn = $(this).prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i>');
$('#disk-loading').show().text('<?php echo $text_loading; ?>...'); $('#disk-loading').show().text('<?php echo $text_loading; ?>...');
var url = 'index.php?route=module/img_opti/get_disk_stats&' + tokenParam; var url = 'index.php?route=extension/module/img_opti/get_disk_stats&' + tokenParam;
if (isManual) { if (isManual) {
url += '&refresh=1'; url += '&refresh=1';
} }
@ -3228,7 +3216,7 @@ $('#import-settings-file').on('change', function() {
var fd = new FormData(); var fd = new FormData();
fd.append('import_json', file); fd.append('import_json', file);
$.ajax({ $.ajax({
url: 'index.php?route=module/img_opti/import_settings&' + tokenParam, url: 'index.php?route=extension/module/img_opti/import_settings&' + tokenParam,
type: 'POST', data: fd, processData: false, contentType: false, dataType: 'json', type: 'POST', data: fd, processData: false, contentType: false, dataType: 'json',
success: function(json) { success: function(json) {
if (json.error) status.html('<span class="text-danger"><i class="fa fa-times"></i> ' + json.error + '</span>'); if (json.error) status.html('<span class="text-danger"><i class="fa fa-times"></i> ' + json.error + '</span>');
@ -3239,24 +3227,20 @@ $('#import-settings-file').on('change', function() {
this.value = ''; this.value = '';
}); });
// ============ PERSIST ACTIVE TAB ============ // ============ PERSIST ACTIVE TAB & TOOL ============
$(document).ready(function() { $(document).ready(function() {
var activeTab = localStorage.getItem('nat_active_tab_img_opti'); var activeTab = localStorage.getItem('nat_active_tab_img_opti');
var $tabLinks = $('.nav-tabs').first().find('a[data-toggle="tab"]');
var tabExists = false;
if (activeTab) { if (activeTab) {
var $targetTab = $('.nav-tabs').first().find('a[href="' + activeTab + '"]'); $('.nav-tabs').first().find('a[href="' + activeTab + '"]').tab('show');
if ($targetTab.length > 0) {
$targetTab.tab('show');
tabExists = true;
}
}
if (!tabExists && $tabLinks.length > 0) {
$tabLinks.first().tab('show');
} }
$('.nav-tabs').first().on('shown.bs.tab', 'a[data-toggle="tab"]', function(e) { $('.nav-tabs').first().on('shown.bs.tab', 'a[data-toggle="tab"]', function(e) {
localStorage.setItem('nat_active_tab_img_opti', $(e.target).attr('href')); localStorage.setItem('nat_active_tab_img_opti', $(e.target).attr('href'));
}); });
var savedTool = localStorage.getItem('nat_selected_tool_img_opti');
if (savedTool && $('#tool-selector option[value="' + savedTool + '"]').length) {
$('#tool-selector').val(savedTool).trigger('change');
}
}); });
</script> </script>
@ -3349,8 +3333,10 @@ $(document).ready(function() {
<label class="col-sm-3 control-label"><?php echo $entry_wm_text_color; ?></label> <label class="col-sm-3 control-label"><?php echo $entry_wm_text_color; ?></label>
<div class="col-sm-9"> <div class="col-sm-9">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon" id="modal-wm-color-preview" style="background:#ffffff; width:36px;">&nbsp;</span> <span class="input-group-addon" style="padding: 3px 8px; background: #f8f9fa;">
<input type="text" id="wm-rule-text-color" class="form-control" placeholder="#ffffff" maxlength="7"/> <input type="color" id="wm-rule-text-color-picker" value="#ffffff" style="width: 28px; height: 24px; border: none; padding: 0; cursor: pointer; background: transparent; vertical-align: middle;" />
</span>
<input type="text" id="wm-rule-text-color" class="form-control" placeholder="#ffffff" maxlength="7" value="#ffffff" />
</div> </div>
</div> </div>
</div> </div>
@ -3453,6 +3439,14 @@ $(document).ready(function() {
<div class="form-group" style="margin-top:10px;"> <div class="form-group" style="margin-top:10px;">
<label class="col-sm-3 control-label"></label> <label class="col-sm-3 control-label"></label>
<div class="col-sm-9"> <div class="col-sm-9">
<div style="margin-bottom: 10px;">
<label style="font-weight:bold; margin-right:8px; display:inline-block; vertical-align:middle;">Фон прев'ю / Фон превью / Preview BG:</label>
<div class="btn-group" data-toggle="buttons" id="wm-preview-bg-switcher">
<label class="btn btn-default active" style="padding: 4px 10px; font-size: 12px;"><input type="radio" name="wm_preview_bg" value="product" checked="checked"> 🖼️ Товар / Item</label>
<label class="btn btn-default" style="padding: 4px 10px; font-size: 12px;"><input type="radio" name="wm_preview_bg" value="white"> ⚪ Білий / Белый</label>
<label class="btn btn-default" style="padding: 4px 10px; font-size: 12px;"><input type="radio" name="wm_preview_bg" value="transparent"> 🏁 Сітка / Сетка</label>
</div>
</div>
<button type="button" id="btn-modal-wm-preview" class="btn btn-info"><i class="fa fa-eye"></i> <?php echo $button_preview_watermark; ?></button> <button type="button" id="btn-modal-wm-preview" class="btn btn-info"><i class="fa fa-eye"></i> <?php echo $button_preview_watermark; ?></button>
<div id="modal-wm-preview-container" style="display:none; margin-top:15px; text-align:center; border:2px dashed #2c3e50; padding:15px; border-radius:4px; background:#f8f9fa;"> <div id="modal-wm-preview-container" style="display:none; margin-top:15px; text-align:center; border:2px dashed #2c3e50; padding:15px; border-radius:4px; background:#f8f9fa;">
<div style="font-weight:bold; color:#2c3e50; margin-bottom:8px;"><i class="fa fa-image"></i> <?php echo $text_wm_preview_title; ?></div> <div style="font-weight:bold; color:#2c3e50; margin-bottom:8px;"><i class="fa fa-image"></i> <?php echo $text_wm_preview_title; ?></div>

View File

@ -634,4 +634,4 @@ class ControllerModuleImgOptiCron extends Controller {
$parts = explode('/', $path); $parts = explode('/', $path);
return end($parts); return end($parts);
} }
} }

View File

@ -464,7 +464,6 @@ class elFinder
*/ */
private static $seekableUrlFps = array(); private static $seekableUrlFps = array();
// Errors messages
const ERROR_ACCESS_DENIED = 'errAccess'; const ERROR_ACCESS_DENIED = 'errAccess';
const ERROR_ARC_MAXSIZE = 'errArcMaxSize'; const ERROR_ARC_MAXSIZE = 'errArcMaxSize';
const ERROR_ARC_SYMLINKS = 'errArcSymlinks'; const ERROR_ARC_SYMLINKS = 'errArcSymlinks';
@ -540,7 +539,6 @@ class elFinder
*/ */
public function __construct($opts) public function __construct($opts)
{ {
// set default_charset
if (version_compare(PHP_VERSION, '5.6', '>=')) { if (version_compare(PHP_VERSION, '5.6', '>=')) {
if (($_val = ini_get('iconv.internal_encoding')) && strtoupper($_val) !== 'UTF-8') { if (($_val = ini_get('iconv.internal_encoding')) && strtoupper($_val) !== 'UTF-8') {
ini_set('iconv.internal_encoding', ''); ini_set('iconv.internal_encoding', '');
@ -561,7 +559,6 @@ class elFinder
} }
ini_set('default_charset', 'UTF-8'); ini_set('default_charset', 'UTF-8');
// define accept constant of server commands path
!defined('ELFINDER_TAR_PATH') && define('ELFINDER_TAR_PATH', 'tar'); !defined('ELFINDER_TAR_PATH') && define('ELFINDER_TAR_PATH', 'tar');
!defined('ELFINDER_GZIP_PATH') && define('ELFINDER_GZIP_PATH', 'gzip'); !defined('ELFINDER_GZIP_PATH') && define('ELFINDER_GZIP_PATH', 'gzip');
!defined('ELFINDER_BZIP2_PATH') && define('ELFINDER_BZIP2_PATH', 'bzip2'); !defined('ELFINDER_BZIP2_PATH') && define('ELFINDER_BZIP2_PATH', 'bzip2');
@ -579,29 +576,21 @@ class elFinder
!defined('ELFINDER_DISABLE_ZIPEDITOR') && define('ELFINDER_DISABLE_ZIPEDITOR', false); !defined('ELFINDER_DISABLE_ZIPEDITOR') && define('ELFINDER_DISABLE_ZIPEDITOR', false);
// enable(true)/disable(false) handling postscript on ImageMagick
// Should be `false` as long as there is a Ghostscript vulnerability
// see https://artifex.com/news/ghostscript-security-resolved/ // see https://artifex.com/news/ghostscript-security-resolved/
!defined('ELFINDER_IMAGEMAGICK_PS') && define('ELFINDER_IMAGEMAGICK_PS', false); !defined('ELFINDER_IMAGEMAGICK_PS') && define('ELFINDER_IMAGEMAGICK_PS', false);
// for backward compat
$this->version = (string)self::$ApiVersion; $this->version = (string)self::$ApiVersion;
// set error handler of WARNING, NOTICE
$errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR; $errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR;
if (defined('E_DEPRECATED')) { if (defined('E_DEPRECATED')) {
$errLevel |= E_DEPRECATED | E_USER_DEPRECATED; $errLevel |= E_DEPRECATED | E_USER_DEPRECATED;
} }
set_error_handler('elFinder::phpErrorHandler', $errLevel); set_error_handler('elFinder::phpErrorHandler', $errLevel);
// Associative array of file pointers to close at the end of script: ['temp file pointer' => true]
$GLOBALS['elFinderTempFps'] = array(); $GLOBALS['elFinderTempFps'] = array();
// Associative array of files to delete at the end of script: ['temp file path' => true]
$GLOBALS['elFinderTempFiles'] = array(); $GLOBALS['elFinderTempFiles'] = array();
// regist Shutdown function
register_shutdown_function(array('elFinder', 'onShutdown')); register_shutdown_function(array('elFinder', 'onShutdown'));
// convert PATH_INFO to GET query
if (!empty($_SERVER['PATH_INFO'])) { if (!empty($_SERVER['PATH_INFO'])) {
$_ps = explode('/', trim($_SERVER['PATH_INFO'], '/')); $_ps = explode('/', trim($_SERVER['PATH_INFO'], '/'));
if (!isset($_GET['cmd'])) { if (!isset($_GET['cmd'])) {
@ -622,15 +611,12 @@ class elFinder
} }
} }
// set elFinder instance
elFinder::$instance = $this; elFinder::$instance = $this;
// setup debug mode
$this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false); $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
if ($this->debug) { if ($this->debug) {
error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL') ? ELFINDER_DEBUG_ERRORLEVEL : -1); error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL') ? ELFINDER_DEBUG_ERRORLEVEL : -1);
ini_set('display_errors', '1'); ini_set('display_errors', '1');
// clear output buffer and stop output filters
while (ob_get_level() && ob_end_clean()) { while (ob_get_level() && ob_end_clean()) {
} }
} }
@ -639,7 +625,6 @@ class elFinder
include_once dirname(__FILE__) . '/elFinderSessionInterface.php'; include_once dirname(__FILE__) . '/elFinderSessionInterface.php';
} }
// session handler
if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) { if (!empty($opts['session']) && $opts['session'] instanceof elFinderSessionInterface) {
$this->session = $opts['session']; $this->session = $opts['session'];
} else { } else {
@ -655,16 +640,13 @@ class elFinder
} }
$this->session = new elFinderSession($sessionOpts); $this->session = new elFinderSession($sessionOpts);
} }
// try session start | restart
$this->session->start(); $this->session->start();
// 'netmount' added to handle requests synchronously on unmount
$sessionUseCmds = array('netmount'); $sessionUseCmds = array('netmount');
if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) { if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) {
$sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']); $sessionUseCmds = array_merge($sessionUseCmds, $opts['sessionUseCmds']);
} }
// set self::$volumesCnt by HTTP header "X-elFinder-VolumesCntStart"
if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) { if (isset($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']) && ($volumesCntStart = intval($_SERVER['HTTP_X_ELFINDER_VOLUMESCNTSTART']))) {
self::$volumesCnt = $volumesCntStart; self::$volumesCnt = $volumesCntStart;
} }
@ -710,41 +692,31 @@ class elFinder
$this->itemLockExpire = intval($opts['itemLockExpire']); $this->itemLockExpire = intval($opts['itemLockExpire']);
} }
// deprecated settings
$this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes'; $this->netVolumesSessionKey = !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes';
self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches'; self::$sessionCacheKey = !empty($opts['sessionCacheKey']) ? $opts['sessionCacheKey'] : 'elFinderCaches';
// check session cache
$_optsMD5 = md5(json_encode($opts['roots'])); $_optsMD5 = md5(json_encode($opts['roots']));
if ($this->session->get('_optsMD5') !== $_optsMD5) { if ($this->session->get('_optsMD5') !== $_optsMD5) {
$this->session->set('_optsMD5', $_optsMD5); $this->session->set('_optsMD5', $_optsMD5);
} }
// setlocale and global locale regists to elFinder::locale
self::$locale = !empty($opts['locale']) ? $opts['locale'] : (substr(PHP_OS, 0, 3) === 'WIN' ? 'C' : 'en_US.UTF-8'); self::$locale = !empty($opts['locale']) ? $opts['locale'] : (substr(PHP_OS, 0, 3) === 'WIN' ? 'C' : 'en_US.UTF-8');
if (false === setlocale(LC_ALL, self::$locale)) { if (false === setlocale(LC_ALL, self::$locale)) {
self::$locale = setlocale(LC_ALL, '0'); self::$locale = setlocale(LC_ALL, '0');
} }
// set defaultMimefile
elFinder::$defaultMimefile = isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : ''; elFinder::$defaultMimefile = isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : '';
// set memoryLimitGD
elFinder::$memoryLimitGD = isset($opts['memoryLimitGD']) ? $opts['memoryLimitGD'] : 0; elFinder::$memoryLimitGD = isset($opts['memoryLimitGD']) ? $opts['memoryLimitGD'] : 0;
// set flag of throwErrorOnExec
// `true` need `try{}` block for `$connector->run();`
$this->throwErrorOnExec = !empty($opts['throwErrorOnExec']); $this->throwErrorOnExec = !empty($opts['throwErrorOnExec']);
// set archivers
elFinder::$archivers = isset($opts['archivers']) && is_array($opts['archivers']) ? $opts['archivers'] : array(); elFinder::$archivers = isset($opts['archivers']) && is_array($opts['archivers']) ? $opts['archivers'] : array();
// set utf8Encoder
if (isset($opts['utf8Encoder']) && is_callable($opts['utf8Encoder'])) { if (isset($opts['utf8Encoder']) && is_callable($opts['utf8Encoder'])) {
$this->utf8Encoder = $opts['utf8Encoder']; $this->utf8Encoder = $opts['utf8Encoder'];
} }
// bind events listeners
if (!empty($opts['bind']) && is_array($opts['bind'])) { if (!empty($opts['bind']) && is_array($opts['bind'])) {
$_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET; $_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
$_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : ''; $_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
@ -754,7 +726,6 @@ class elFinder
$doRegist = ($_reqCmd && in_array($_reqCmd, array_map('self::getCmdOfBind', explode(' ', $cmd)))); $doRegist = ($_reqCmd && in_array($_reqCmd, array_map('self::getCmdOfBind', explode(' ', $cmd))));
} }
if ($doRegist) { if ($doRegist) {
// for backward compatibility
if (!is_array($handlers)) { if (!is_array($handlers)) {
$handlers = array($handlers); $handlers = array($handlers);
} else { } else {
@ -785,16 +756,13 @@ class elFinder
$opts['roots'] = array(); $opts['roots'] = array();
} }
// try to enable elFinderVolumeFlysystemZipArchiveNetmount to zip editing
if (empty(elFinder::$netDrivers['ziparchive'])) { if (empty(elFinder::$netDrivers['ziparchive'])) {
elFinder::$netDrivers['ziparchive'] = 'FlysystemZipArchiveNetmount'; elFinder::$netDrivers['ziparchive'] = 'FlysystemZipArchiveNetmount';
} }
// check for net volumes stored in session
$netVolumes = $this->getNetVolumes(); $netVolumes = $this->getNetVolumes();
foreach ($netVolumes as $key => $root) { foreach ($netVolumes as $key => $root) {
if (!isset($root['id'])) { if (!isset($root['id'])) {
// given fixed unique id
if (!$root['id'] = $this->getNetVolumeUniqueId($netVolumes)) { if (!$root['id'] = $this->getNetVolumeUniqueId($netVolumes)) {
$this->mountErrors[] = 'Netmount Driver "' . $root['driver'] . '" : Could\'t given volume id.'; $this->mountErrors[] = 'Netmount Driver "' . $root['driver'] . '" : Could\'t given volume id.';
continue; continue;
@ -804,25 +772,21 @@ class elFinder
$opts['roots'][$key] = $root; $opts['roots'][$key] = $root;
} }
// "mount" volumes
foreach ($opts['roots'] as $i => $o) { foreach ($opts['roots'] as $i => $o) {
$class = 'elFinderVolume' . (isset($o['driver']) ? $o['driver'] : ''); $class = 'elFinderVolume' . (isset($o['driver']) ? $o['driver'] : '');
if (class_exists($class)) { if (class_exists($class)) {
/* @var elFinderVolumeDriver $volume */
$volume = new $class(); $volume = new $class();
try { try {
if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) { if ($this->maxArcFilesSize && (empty($o['maxArcFilesSize']) || $this->maxArcFilesSize < $o['maxArcFilesSize'])) {
$o['maxArcFilesSize'] = $this->maxArcFilesSize; $o['maxArcFilesSize'] = $this->maxArcFilesSize;
} }
// pass session handler
$volume->setSession($this->session); $volume->setSession($this->session);
if (!$this->default) { if (!$this->default) {
$volume->setNeedOnline(true); $volume->setNeedOnline(true);
} }
if ($volume->mount($o)) { if ($volume->mount($o)) {
// unique volume id (ends on "_") - used as prefix to files hash
$id = $volume->id(); $id = $volume->id();
$this->volumes[$id] = $volume; $this->volumes[$id] = $volume;
@ -849,10 +813,8 @@ class elFinder
} }
} }
// if at least one readable volume - ii desu >_<
$this->loaded = !empty($this->default); $this->loaded = !empty($this->default);
// restore error handler for now
restore_error_handler(); restore_error_handler();
} }
@ -1067,10 +1029,8 @@ class elFinder
**/ **/
public function exec($cmd, $args) public function exec($cmd, $args)
{ {
// set error handler of WARNING, NOTICE
set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE); set_error_handler('elFinder::phpErrorHandler', E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE);
// set current request args
self::$currentArgs = $args; self::$currentArgs = $args;
if (!$this->loaded) { if (!$this->loaded) {
@ -1085,16 +1045,13 @@ class elFinder
return array('error' => $this->error(self::ERROR_UNKNOWN_CMD)); return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
} }
// check request id
$args['reqid'] = preg_replace('[^0-9a-fA-F]', '', !empty($args['reqid']) ? $args['reqid'] : (!empty($_SERVER['HTTP_X_ELFINDERREQID']) ? $_SERVER['HTTP_X_ELFINDERREQID'] : '')); $args['reqid'] = preg_replace('[^0-9a-fA-F]', '', !empty($args['reqid']) ? $args['reqid'] : (!empty($_SERVER['HTTP_X_ELFINDERREQID']) ? $_SERVER['HTTP_X_ELFINDERREQID'] : ''));
// to abort this request
if ($cmd === 'abort') { if ($cmd === 'abort') {
$this->abort($args); $this->abort($args);
return array('error' => 0); return array('error' => 0);
} }
// make flag file and set self::$abortCheckFile
if ($args['reqid']) { if ($args['reqid']) {
$this->abort(array('makeFile' => $args['reqid'])); $this->abort(array('makeFile' => $args['reqid']));
} }
@ -1105,10 +1062,8 @@ class elFinder
} }
} }
// regist shutdown function as fallback
register_shutdown_function(array($this, 'itemAutoUnlock')); register_shutdown_function(array($this, 'itemAutoUnlock'));
// detect destination dirHash and volume
$dstVolume = false; $dstVolume = false;
$dst = !empty($args['target']) ? $args['target'] : (!empty($args['dst']) ? $args['dst'] : ''); $dst = !empty($args['target']) ? $args['target'] : (!empty($args['dst']) ? $args['dst'] : '');
if ($dst) { if ($dst) {
@ -1122,14 +1077,12 @@ class elFinder
$dst = ''; $dst = '';
} }
} else if ($cmd === 'open') { } else if ($cmd === 'open') {
// for initial open without args `target`
$dstVolume = $this->default; $dstVolume = $this->default;
$dst = $dstVolume->defaultPath(); $dst = $dstVolume->defaultPath();
} }
$result = null; $result = null;
// call pre handlers for this command
$args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd]) ? false : $this->sessionCloseEarlier; $args['sessionCloseEarlier'] = isset($this->sessionUseCmds[$cmd]) ? false : $this->sessionCloseEarlier;
if (!empty($this->listeners[$cmd . '.pre'])) { if (!empty($this->listeners[$cmd . '.pre'])) {
foreach ($this->listeners[$cmd . '.pre'] as $handler) { foreach ($this->listeners[$cmd . '.pre'] as $handler) {
@ -1152,15 +1105,12 @@ class elFinder
} }
} }
// unlock session data for multiple access
if ($this->sessionCloseEarlier && $args['sessionCloseEarlier']) { if ($this->sessionCloseEarlier && $args['sessionCloseEarlier']) {
$this->session->close(); $this->session->close();
// deprecated property
elFinder::$sessionClosed = true; elFinder::$sessionClosed = true;
} }
if (substr(PHP_OS, 0, 3) === 'WIN') { if (substr(PHP_OS, 0, 3) === 'WIN') {
// set time out
elFinder::extendTimeLimit(300); elFinder::extendTimeLimit(300);
} }
@ -1180,7 +1130,6 @@ class elFinder
} }
} }
// check change dstDir
$changeDst = false; $changeDst = false;
if ($dst && $dstVolume && (!empty($result['added']) || !empty($result['removed']))) { if ($dst && $dstVolume && (!empty($result['added']) || !empty($result['removed']))) {
$changeDst = true; $changeDst = true;
@ -1210,7 +1159,6 @@ class elFinder
$volume->resetResultStat(); $volume->resetResultStat();
} }
// dstDir is changed
if ($changeDst) { if ($changeDst) {
if ($dstDir = $dstVolume->dir($dst)) { if ($dstDir = $dstVolume->dir($dst)) {
if (!isset($result['changed'])) { if (!isset($result['changed'])) {
@ -1220,17 +1168,14 @@ class elFinder
} }
} }
// call handlers for this command
if (!empty($this->listeners[$cmd])) { if (!empty($this->listeners[$cmd])) {
foreach ($this->listeners[$cmd] as $handler) { foreach ($this->listeners[$cmd] as $handler) {
if (call_user_func_array($handler, array($cmd, &$result, $args, $this, $dstVolume))) { if (call_user_func_array($handler, array($cmd, &$result, $args, $this, $dstVolume))) {
// handler return true to force sync client after command completed
$result['sync'] = true; $result['sync'] = true;
} }
} }
} }
// replace removed files info with removed files hashes
if (!empty($result['removed'])) { if (!empty($result['removed'])) {
$removed = array(); $removed = array();
foreach ($result['removed'] as $file) { foreach ($result['removed'] as $file) {
@ -1238,15 +1183,12 @@ class elFinder
} }
$result['removed'] = array_unique($removed); $result['removed'] = array_unique($removed);
} }
// remove hidden files and filter files by mimetypes
if (!empty($result['added'])) { if (!empty($result['added'])) {
$result['added'] = $this->filter($result['added']); $result['added'] = $this->filter($result['added']);
} }
// remove hidden files and filter files by mimetypes
if (!empty($result['changed'])) { if (!empty($result['changed'])) {
$result['changed'] = $this->filter($result['changed']); $result['changed'] = $this->filter($result['changed']);
} }
// add toasts
if ($this->toastMessages) { if ($this->toastMessages) {
$result['toasts'] = array_merge(((isset($result['toasts']) && is_array($result['toasts']))? $result['toasts'] : array()), $this->toastMessages); $result['toasts'] = array_merge(((isset($result['toasts']) && is_array($result['toasts']))? $result['toasts'] : array()), $this->toastMessages);
} }
@ -1267,7 +1209,6 @@ class elFinder
} }
} }
// remove sesstion var 'urlContentSaveIds'
if ($this->removeContentSaveIds) { if ($this->removeContentSaveIds) {
$urlContentSaveIds = $this->session->get('urlContentSaveIds', array()); $urlContentSaveIds = $this->session->get('urlContentSaveIds', array());
foreach (array_keys($this->removeContentSaveIds) as $contentSaveId) { foreach (array_keys($this->removeContentSaveIds) as $contentSaveId) {
@ -1287,10 +1228,8 @@ class elFinder
$volume->umount(); $volume->umount();
} }
// unlock locked items
$this->itemAutoUnlock(); $this->itemAutoUnlock();
// custom data
if ($this->customData !== null) { if ($this->customData !== null) {
$result['customData'] = $this->customData ? json_encode($this->customData) : ''; $result['customData'] = $this->customData ? json_encode($this->customData) : '';
} }
@ -1456,7 +1395,6 @@ class elFinder
$key = strtolower($name); $key = strtolower($name);
if (!isset($this->plugins[$key])) { if (!isset($this->plugins[$key])) {
$class = 'elFinderPlugin' . $name; $class = 'elFinderPlugin' . $name;
// to try auto load
if (!class_exists($class)) { if (!class_exists($class)) {
$p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php'; $p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
if (is_file($p_file)) { if (is_file($p_file)) {
@ -1472,9 +1410,6 @@ class elFinder
return $this->plugins[$key]; return $this->plugins[$key];
} }
/***************************************************************************/
/* commands */
/***************************************************************************/
/** /**
* Normalize error messages * Normalize error messages
@ -1541,10 +1476,8 @@ class elFinder
} }
} }
/* @var elFinderVolumeDriver $volume */
$volume = new $class(); $volume = new $class();
// pass session handler
$volume->setSession($this->session); $volume->setSession($this->session);
$volume->setNeedOnline(true); $volume->setNeedOnline(true);
@ -1566,13 +1499,11 @@ class elFinder
$netVolumes = $this->getNetVolumes(); $netVolumes = $this->getNetVolumes();
if (!isset($options['id'])) { if (!isset($options['id'])) {
// given fixed unique id
if (!$options['id'] = $this->getNetVolumeUniqueId($netVolumes)) { if (!$options['id'] = $this->getNetVolumeUniqueId($netVolumes)) {
return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], 'Could\'t given volume id.')); return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], 'Could\'t given volume id.'));
} }
} }
// load additional volume root options
if (!empty($this->optionsNetVolumes['*'])) { if (!empty($this->optionsNetVolumes['*'])) {
$options = array_merge($this->optionsNetVolumes['*'], $options); $options = array_merge($this->optionsNetVolumes['*'], $options);
} }
@ -1586,7 +1517,6 @@ class elFinder
$options['netkey'] = $key; $options['netkey'] = $key;
if (!isset($netVolumes[$key]) && $volume->mount($options)) { if (!isset($netVolumes[$key]) && $volume->mount($options)) {
// call post-process function of netmount
if (is_callable(array($volume, 'postNetmount'))) { if (is_callable(array($volume, 'postNetmount'))) {
$volume->postNetmount($options); $volume->postNetmount($options);
} }
@ -1630,9 +1560,6 @@ class elFinder
$hash = $init ? 'default folder' : '#' . $target; $hash = $init ? 'default folder' : '#' . $target;
$compare = ''; $compare = '';
// on init request we can get invalid dir hash -
// dir which can not be opened now, but remembered by client,
// so open default dir
if ((!$cwd || !$cwd['read']) && $init) { if ((!$cwd || !$cwd['read']) && $init) {
$volume = $this->default; $volume = $this->default;
$target = $volume->defaultPath(); $target = $volume->defaultPath();
@ -1648,7 +1575,6 @@ class elFinder
$files = array(); $files = array();
// get current working directory files list
if (($ls = $volume->scandir($cwd['hash'])) === false) { if (($ls = $volume->scandir($cwd['hash'])) === false) {
return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error())); return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
} }
@ -1657,14 +1583,12 @@ class elFinder
$cwd = $volume->dir($target); $cwd = $volume->dir($target);
} }
// get other volume root
if ($tree) { if ($tree) {
foreach ($this->volumes as $id => $v) { foreach ($this->volumes as $id => $v) {
$files[] = $v->file($v->root()); $files[] = $v->file($v->root());
} }
} }
// long polling mode
if ($args['compare']) { if ($args['compare']) {
$sleep = max(1, (int)$volume->getOption('lsPlSleep')); $sleep = max(1, (int)$volume->getOption('lsPlSleep'));
$standby = (int)$volume->getOption('plStandby'); $standby = (int)$volume->getOption('plStandby');
@ -1840,7 +1764,6 @@ class elFinder
$CriOS = isset($_SERVER['HTTP_USER_AGENT'])? (strpos($_SERVER['HTTP_USER_AGENT'], 'CriOS') !== false) : false; $CriOS = isset($_SERVER['HTTP_USER_AGENT'])? (strpos($_SERVER['HTTP_USER_AGENT'], 'CriOS') !== false) : false;
if (!$download) { if (!$download) {
//1st: Return array contains download archive file info
$error = array(self::ERROR_ARCHIVE); $error = array(self::ERROR_ARCHIVE);
if (($volume = $this->volume($targets[0])) !== false) { if (($volume = $this->volume($targets[0])) !== false) {
if ($dlres = $volume->zipdl($targets)) { if ($dlres = $volume->zipdl($targets)) {
@ -1867,10 +1790,7 @@ class elFinder
} }
return array('error' => $error); return array('error' => $error);
} else { } else {
// 2nd: Return array contains opened file session key, root itself and required headers
// Detect Chrome on iOS
// It has access twice on downloading
$CriOSinit = false; $CriOSinit = false;
if ($CriOS) { if ($CriOS) {
$accept = isset($_SERVER['HTTP_ACCEPT'])? $_SERVER['HTTP_ACCEPT'] : ''; $accept = isset($_SERVER['HTTP_ACCEPT'])? $_SERVER['HTTP_ACCEPT'] : '';
@ -1878,15 +1798,12 @@ class elFinder
$CriOSinit = true; $CriOSinit = true;
} }
} }
// data check
if (count($targets) !== 4 || ($volume = $this->volume($targets[0])) == false || !($file = $CriOS? $targets[1] : $this->session->get('zipdl' . $targets[1]))) { if (count($targets) !== 4 || ($volume = $this->volume($targets[0])) == false || !($file = $CriOS? $targets[1] : $this->session->get('zipdl' . $targets[1]))) {
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
} }
$path = $volume->getTempPath() . DIRECTORY_SEPARATOR . basename($file); $path = $volume->getTempPath() . DIRECTORY_SEPARATOR . basename($file);
// remove session data of "zipdl..."
$this->session->remove('zipdl' . $targets[1]); $this->session->remove('zipdl' . $targets[1]);
if (!$CriOSinit) { if (!$CriOSinit) {
// register auto delete on shutdown
$GLOBALS['elFinderTempFiles'][$path] = true; $GLOBALS['elFinderTempFiles'][$path] = true;
} }
if ($volume->commandDisabled('zipdl')) { if ($volume->commandDisabled('zipdl')) {
@ -1895,7 +1812,6 @@ class elFinder
if (!is_readable($path) || !is_writable($path)) { if (!is_readable($path) || !is_writable($path)) {
return array('error' => 'File not found', 'header' => $h404, 'raw' => true); return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
} }
// for HTTP headers
$name = $targets[2]; $name = $targets[2];
$mime = $targets[3]; $mime = $targets[3];
@ -1926,7 +1842,6 @@ class elFinder
'Connection: close' 'Connection: close'
) )
); );
// add cache control headers
if ($cacheHeaders = $volume->getOption('cacheHeaders')) { if ($cacheHeaders = $volume->getOption('cacheHeaders')) {
$result['header'] = array_merge($result['header'], $cacheHeaders); $result['header'] = array_merge($result['header'], $cacheHeaders);
} }
@ -1949,7 +1864,6 @@ class elFinder
$target = $args['target']; $target = $args['target'];
$download = !empty($args['download']); $download = !empty($args['download']);
$onetime = !empty($args['onetime']); $onetime = !empty($args['onetime']);
//$h304 = 'HTTP/1.1 304 Not Modified';
$h403 = 'HTTP/1.0 403 Access Denied'; $h403 = 'HTTP/1.0 403 Access Denied';
$a403 = array('error' => 'Access Denied', 'header' => $h403, 'raw' => true); $a403 = array('error' => 'Access Denied', 'header' => $h403, 'raw' => true);
$h404 = 'HTTP/1.0 404 Not Found'; $h404 = 'HTTP/1.0 404 Not Found';
@ -2002,10 +1916,8 @@ class elFinder
} }
} }
// check aborted by user
elFinder::checkAborted(); elFinder::checkAborted();
// allow change MIME type by 'file.pre' callback functions
$mime = isset($args['mime']) ? $args['mime'] : $file['mime']; $mime = isset($args['mime']) ? $args['mime'] : $file['mime'];
if ($download || $onetime) { if ($download || $onetime) {
$disp = 'attachment'; $disp = 'attachment';
@ -2059,12 +1971,10 @@ class elFinder
); );
if (!$onetime) { if (!$onetime) {
// add cache control headers
if ($cacheHeaders = $volume->getOption('cacheHeaders')) { if ($cacheHeaders = $volume->getOption('cacheHeaders')) {
$result['header'] = array_merge($result['header'], $cacheHeaders); $result['header'] = array_merge($result['header'], $cacheHeaders);
} }
// check 'xsendfile'
$xsendfile = $volume->getOption('xsendfile'); $xsendfile = $volume->getOption('xsendfile');
$path = null; $path = null;
if ($xsendfile) { if ($xsendfile) {
@ -2087,7 +1997,6 @@ class elFinder
} }
} }
// add "Content-Location" if file has url data
if (isset($file['url']) && $file['url'] && $file['url'] != 1) { if (isset($file['url']) && $file['url'] && $file['url'] != 1) {
$result['header'][] = 'Content-Location: ' . $file['url']; $result['header'][] = 'Content-Location: ' . $file['url'];
} }
@ -2282,7 +2191,6 @@ class elFinder
} }
if ($query) { if ($query) {
// batch rename
$splits = elFinder::splitFileExtention($query); $splits = elFinder::splitFileExtention($query);
if ($splits[1] && $splits[0] === '*') { if ($splits[1] && $splits[0] === '*') {
$type = 'extention'; $type = 'extention';
@ -2475,7 +2383,6 @@ class elFinder
*/ */
protected function editor($args = array()) protected function editor($args = array())
{ {
/* @var elFinderEditor $editor */
$name = $args['name']; $name = $args['name'];
if (is_array($name)) { if (is_array($name)) {
$res = array(); $res = array();
@ -2547,27 +2454,21 @@ class elFinder
if (preg_match('~^(?:ht|f)tps?://[-_.!\~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+~i', $url)) { if (preg_match('~^(?:ht|f)tps?://[-_.!\~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+~i', $url)) {
$info = parse_url($url); $info = parse_url($url);
$host = trim(strtolower($info['host']), '.'); $host = trim(strtolower($info['host']), '.');
// do not support IPv6 address
if (preg_match('/^\[.*\]$/', $host)) { if (preg_match('/^\[.*\]$/', $host)) {
return false; return false;
} }
// do not support non dot host
if (strpos($host, '.') === false) { if (strpos($host, '.') === false) {
return false; return false;
} }
// do not support URL-encoded host
if (strpos($host, '%') !== false) { if (strpos($host, '%') !== false) {
return false; return false;
} }
// disallow including "localhost" and "localdomain"
if (preg_match('/\b(?:localhost|localdomain)\b/', $host)) { if (preg_match('/\b(?:localhost|localdomain)\b/', $host)) {
return false; return false;
} }
// wildcard DNS (e.g xip.io)
if (preg_match('/0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}/', $host)) { if (preg_match('/0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}/', $host)) {
$host = gethostbyname($host); $host = gethostbyname($host);
} }
// check IPv4 local loopback, private network and link local
if (preg_match('/^0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}$/', $host, $m)) { if (preg_match('/^0x[0-9a-f]+|[0-9]+(?:\.(?:0x[0-9a-f]+|[0-9]+)){1,3}$/', $host, $m)) {
$long = (int)sprintf('%u', ip2long($host)); $long = (int)sprintf('%u', ip2long($host));
if (!$long) { if (!$long) {
@ -2583,9 +2484,7 @@ class elFinder
return false; return false;
} }
} }
// dose not support 'user' and 'pass' for security reasons
$url = $info['scheme'].'://'.$host.(!empty($info['port'])? (':'.$info['port']) : '').$info['path'].(!empty($info['query'])? ('?'.$info['query']) : '').(!empty($info['fragment'])? ('#'.$info['fragment']) : ''); $url = $info['scheme'].'://'.$host.(!empty($info['port'])? (':'.$info['port']) : '').$info['path'].(!empty($info['query'])? ('?'.$info['query']) : '').(!empty($info['fragment'])? ('#'.$info['fragment']) : '');
// check by URL upload filter
if ($this->urlUploadFilter && is_callable($this->urlUploadFilter)) { if ($this->urlUploadFilter && is_callable($this->urlUploadFilter)) {
if (!call_user_func_array($this->urlUploadFilter, array($url, $this))) { if (!call_user_func_array($this->urlUploadFilter, array($url, $this))) {
return false; return false;
@ -2664,16 +2563,13 @@ class elFinder
$arr = parse_url($url); $arr = parse_url($url);
if (!$arr) { if (!$arr) {
// Bad request
return false; return false;
} }
if ($arr['scheme'] === 'https') { if ($arr['scheme'] === 'https') {
$ssl = 'ssl://'; $ssl = 'ssl://';
} }
// query
$arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : ''; $arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : '';
// port
$port = isset($arr['port']) ? $arr['port'] : ''; $port = isset($arr['port']) ? $arr['port'] : '';
$arr['port'] = $port ? $port : ($ssl ? 443 : 80); $arr['port'] = $port ? $port : ($ssl ? 443 : 80);
@ -2736,7 +2632,6 @@ class elFinder
$rc = (int)$rccd[1]; $rc = (int)$rccd[1];
$ret = false; $ret = false;
// Redirect
switch ($rc) { switch ($rc) {
case 307: // Temporary Redirect case 307: // Temporary Redirect
case 303: // See Other case 303: // See Other
@ -2748,10 +2643,8 @@ class elFinder
$url = trim($matches[1]); $url = trim($matches[1]);
if (!preg_match('/^https?:\//', $url)) { // no scheme if (!preg_match('/^https?:\//', $url)) { // no scheme
if ($url[0] != '/') { // Relative path if ($url[0] != '/') { // Relative path
// to Absolute path
$url = substr($url_path, 0, strrpos($url_path, '/')) . '/' . $url; $url = substr($url_path, 0, strrpos($url_path, '/')) . '/' . $url;
} }
// add sheme,host
$url = $url_base . $url; $url = $url_base . $url;
} }
if ($_url === $url) { if ($_url === $url) {
@ -2809,7 +2702,6 @@ class elFinder
protected function parse_data_scheme($str, $extTable, $args = null) protected function parse_data_scheme($str, $extTable, $args = null)
{ {
$data = $name = $mime = ''; $data = $name = $mime = '';
// Scheme 'data://' require `allow_url_fopen` and `allow_url_include`
if ($fp = fopen('data://' . substr($str, 5), 'rb')) { if ($fp = fopen('data://' . substr($str, 5), 'rb')) {
if ($data = stream_get_contents($fp)) { if ($data = stream_get_contents($fp)) {
$meta = stream_get_meta_data($fp); $meta = stream_get_meta_data($fp);
@ -2824,7 +2716,6 @@ class elFinder
} }
if ($data) { if ($data) {
$ext = ($mime && isset($extTable[$mime])) ? '.' . $extTable[$mime] : ''; $ext = ($mime && isset($extTable[$mime])) ? '.' . $extTable[$mime] : '';
// Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data
if (is_array($args['name']) && isset($args['name'][0])) { if (is_array($args['name']) && isset($args['name'][0])) {
$name = $args['name'][0]; $name = $args['name'][0];
if ($ext) { if ($ext) {
@ -2887,7 +2778,6 @@ class elFinder
$mime = trim($mime[0]); $mime = trim($mime[0]);
if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) { if (in_array($mime, array('application/x-empty', 'inode/x-empty'))) {
// finfo return this mime for empty files
$mime = 'text/plain'; $mime = 'text/plain';
} elseif ($mime == 'application/x-zip') { } elseif ($mime == 'application/x-zip') {
// http://elrte.org/redmine/issues/163 // http://elrte.org/redmine/issues/163
@ -3022,7 +2912,6 @@ class elFinder
*/ */
private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null) private function checkChunkedFile($tmpname, $chunk, $cid, $tempDir, $volume = null)
{ {
/* @var elFinderVolumeDriver $volume */
if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) { if (preg_match('/^(.+)(\.\d+_(\d+))\.part$/s', $chunk, $m)) {
$fname = $m[1]; $fname = $m[1];
$encname = md5($cid . '_' . $fname); $encname = md5($cid . '_' . $fname);
@ -3030,7 +2919,6 @@ class elFinder
$clast = intval($m[3]); $clast = intval($m[3]);
if (is_null($tmpname)) { if (is_null($tmpname)) {
ignore_user_abort(true); ignore_user_abort(true);
// chunked file upload fail
foreach (glob($base . '*') as $cf) { foreach (glob($base . '*') as $cf) {
unlink($cf); unlink($cf);
} }
@ -3048,12 +2936,10 @@ class elFinder
$tmpExists = is_file($tmp); $tmpExists = is_file($tmp);
if (!$tmpExists) { if (!$tmpExists) {
// check upload max size
$uploadMaxSize = $volume ? $volume->getUploadMaxSize() : 0; $uploadMaxSize = $volume ? $volume->getUploadMaxSize() : 0;
if ($uploadMaxSize > 0 && $size > $uploadMaxSize) { if ($uploadMaxSize > 0 && $size > $uploadMaxSize) {
return array(self::ERROR_UPLOAD_FILE_SIZE, false); return array(self::ERROR_UPLOAD_FILE_SIZE, false);
} }
// make temp file
$ok = false; $ok = false;
if ($fp = fopen($tmp, 'wb')) { if ($fp = fopen($tmp, 'wb')) {
flock($fp, LOCK_EX); flock($fp, LOCK_EX);
@ -3067,7 +2953,6 @@ class elFinder
return array(self::ERROR_UPLOAD_TEMP, false); return array(self::ERROR_UPLOAD_TEMP, false);
} }
} else { } else {
// wait until makeing temp file (for anothor session)
$cnt = 1200; // Time limit 120 sec $cnt = 1200; // Time limit 120 sec
while (!is_file($base) && --$cnt) { while (!is_file($base) && --$cnt) {
usleep(100000); // wait 100ms usleep(100000); // wait 100ms
@ -3077,12 +2962,10 @@ class elFinder
} }
} }
// check size info
if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) { if ($len != $csize || $start + $len > $size || ($tmpExists && $size != filesize($tmp))) {
return array(self::ERROR_UPLOAD_TEMP, false); return array(self::ERROR_UPLOAD_TEMP, false);
} }
// write chunk data
$src = fopen($tmpname, 'rb'); $src = fopen($tmpname, 'rb');
$fp = fopen($tmp, 'cb'); $fp = fopen($tmp, 'cb');
fseek($fp, $start); fseek($fp, $start);
@ -3091,7 +2974,6 @@ class elFinder
fclose($src); fclose($src);
try { try {
// to check connection is aborted
elFinder::checkAborted(); elFinder::checkAborted();
} catch (elFinderAbortException $e) { } catch (elFinderAbortException $e) {
unlink($tmpname); unlink($tmpname);
@ -3104,16 +2986,13 @@ class elFinder
return array(self::ERROR_UPLOAD_TEMP, false); return array(self::ERROR_UPLOAD_TEMP, false);
} }
// write counts
file_put_contents($base, "\0", FILE_APPEND | LOCK_EX); file_put_contents($base, "\0", FILE_APPEND | LOCK_EX);
if (filesize($base) >= $clast + 1) { if (filesize($base) >= $clast + 1) {
// Completion
unlink($base); unlink($base);
return array($tmp, $fname); return array($tmp, $fname);
} }
} else { } else {
// old way
$part = $base . $m[2]; $part = $base . $m[2];
if (move_uploaded_file($tmpname, $part)) { if (move_uploaded_file($tmpname, $part)) {
chmod($part, 0600); chmod($part, 0600);
@ -3182,7 +3061,6 @@ class elFinder
return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)), $header); return array_merge(array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#' . $target)), $header);
} }
// check $chunk
if (strpos($chunk, '/') !== false || strpos($chunk, '\\') !== false) { if (strpos($chunk, '/') !== false || strpos($chunk, '\\') !== false) {
return array('error' => $this->error(self::ERROR_UPLOAD)); return array('error' => $this->error(self::ERROR_UPLOAD));
} }
@ -3205,14 +3083,12 @@ class elFinder
$this->itemLock($target); $this->itemLock($target);
// file extentions table by MIME
$extTable = array_flip(array_unique($volume->getMimeTable())); $extTable = array_flip(array_unique($volume->getMimeTable()));
if (empty($files)) { if (empty($files)) {
if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) { if (isset($args['upload']) && is_array($args['upload']) && ($tempDir = $this->getTempDir($volume->getTempPath()))) {
$names = array(); $names = array();
foreach ($args['upload'] as $i => $url) { foreach ($args['upload'] as $i => $url) {
// check chunked file upload commit
if ($chunk) { if ($chunk) {
if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') { if ($url === 'chunkfail' && $args['mimes'] === 'chunkfail') {
$this->checkChunkedFile(null, $chunk, $cid, $tempDir); $this->checkChunkedFile(null, $chunk, $cid, $tempDir);
@ -3234,13 +3110,11 @@ class elFinder
$GLOBALS['elFinderTempFiles'][$tmpfname] = true; $GLOBALS['elFinderTempFiles'][$tmpfname] = true;
$_name = ''; $_name = '';
// check is data:
if (substr($url, 0, 5) === 'data:') { if (substr($url, 0, 5) === 'data:') {
list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable, $args); list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable, $args);
} else { } else {
$fp = fopen($tmpfname, 'wb'); $fp = fopen($tmpfname, 'wb');
if ($data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp)) { if ($data = $this->get_remote_contents($url, 30, 5, 'Mozilla/5.0', $fp)) {
// to check connection is aborted
try { try {
elFinder::checkAborted(); elFinder::checkAborted();
} catch(elFinderAbortException $e) { } catch(elFinderAbortException $e) {
@ -3248,7 +3122,6 @@ class elFinder
throw $e; throw $e;
} }
$_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url)); $_name = preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
// Check `Content-Disposition` response header
if (($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) { if (($headers = get_headers($url, true)) && !empty($headers['Content-Disposition'])) {
if (preg_match('/filename\*=(?:([a-zA-Z0-9_-]+?)\'\')"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) { if (preg_match('/filename\*=(?:([a-zA-Z0-9_-]+?)\'\')"?([a-z0-9_.~%-]+)"?/i', $headers['Content-Disposition'], $m)) {
$_name = rawurldecode($m[2]); $_name = rawurldecode($m[2]);
@ -3294,7 +3167,6 @@ class elFinder
$files['tmp_name'][$i] = $tmpfname; $files['tmp_name'][$i] = $tmpfname;
$files['name'][$i] = $_name; $files['name'][$i] = $_name;
$files['error'][$i] = 0; $files['error'][$i] = 0;
// set to auto rename
$volume->setUploadOverwrite(false); $volume->setUploadOverwrite(false);
} else { } else {
unlink($tmpfname); unlink($tmpfname);
@ -3342,12 +3214,10 @@ class elFinder
} }
return $result; return $result;
} else { } else {
// for form clipboard with Google Chrome or Opera
$name = 'image.png'; $name = 'image.png';
} }
} }
// Set name if name eq 'image.png' and $args has 'name' array, e.g. clipboard data
if (strtolower(substr($name, 0, 5)) === 'image' && is_array($args['name']) && isset($args['name'][$i])) { if (strtolower(substr($name, 0, 5)) === 'image' && is_array($args['name']) && isset($args['name'][$i])) {
$type = $files['type'][$i]; $type = $files['type'][$i];
$name = $args['name'][$i]; $name = $args['name'][$i];
@ -3358,7 +3228,6 @@ class elFinder
$name .= $ext; $name .= $ext;
} }
// do hook function 'upload.presave'
try { try {
$this->trigger('upload.presave', array(&$thash, &$name, $tmpname, $this, $volume), $errors); $this->trigger('upload.presave', array(&$thash, &$name, $tmpname, $this, $volume), $errors);
} catch (elFinderTriggerException $e) { } catch (elFinderTriggerException $e) {
@ -3370,7 +3239,6 @@ class elFinder
clearstatcache(); clearstatcache();
if ($mtime && is_file($tmpname)) { if ($mtime && is_file($tmpname)) {
// for keep timestamp option in the LocalFileSystem volume
touch($tmpname, $mtime); touch($tmpname, $mtime);
} }
@ -3391,7 +3259,6 @@ class elFinder
if (!isset($addedDirs[$thash])) { if (!isset($addedDirs[$thash])) {
$addedDirs[$thash] = true; $addedDirs[$thash] = true;
$result['added'][] = $dir; $result['added'][] = $dir;
// to support multi-level directory creation
$_phash = isset($dir['phash']) ? $dir['phash'] : null; $_phash = isset($dir['phash']) ? $dir['phash'] : null;
while ($_phash && !isset($addedDirs[$_phash]) && $_phash !== $target) { while ($_phash && !isset($addedDirs[$_phash]) && $_phash !== $target) {
if ($_dir = $volume->dir($_phash)) { if ($_dir = $volume->dir($_phash)) {
@ -3409,7 +3276,6 @@ class elFinder
} }
} else { } else {
$_target = $target; $_target = $target;
// file rename for backup
if (isset($renames[$name])) { if (isset($renames[$name])) {
$dir = $volume->realpath($_target); $dir = $volume->realpath($_target);
if (isset($hashes[$name])) { if (isset($hashes[$name])) {
@ -3601,7 +3467,6 @@ class elFinder
$enc = ''; $enc = '';
if ($content !== '') { if ($content !== '') {
if (!$args['conv'] || $args['conv'] == '1') { if (!$args['conv'] || $args['conv'] == '1') {
// detect encoding
if (function_exists('mb_detect_encoding')) { if (function_exists('mb_detect_encoding')) {
if ($enc = mb_detect_encoding($content, mb_detect_order(), true)) { if ($enc = mb_detect_encoding($content, mb_detect_order(), true)) {
$encu = strtoupper($enc); $encu = strtoupper($enc);
@ -3620,7 +3485,6 @@ class elFinder
$enc = 'unknown'; $enc = 'unknown';
} }
} }
// call callbacks 'get.detectencoding'
if (!empty($this->listeners['get.detectencoding'])) { if (!empty($this->listeners['get.detectencoding'])) {
foreach ($this->listeners['get.detectencoding'] as $handler) { foreach ($this->listeners['get.detectencoding'] as $handler) {
call_user_func_array($handler, array('get', &$enc, array_merge($args, array('content' => $content)), $this, $volume)); call_user_func_array($handler, array('get', &$enc, array_merge($args, array('content' => $content)), $this, $volume));
@ -3701,7 +3565,6 @@ class elFinder
'content' => $content 'content' => $content
); );
// add cache control headers
if ($cacheHeaders = $volume->getOption('cacheHeaders')) { if ($cacheHeaders = $volume->getOption('cacheHeaders')) {
$res['header'] = array_merge($res['header'], $cacheHeaders); $res['header'] = array_merge($res['header'], $cacheHeaders);
} }
@ -3734,7 +3597,6 @@ class elFinder
if ($encoding === 'scheme') { if ($encoding === 'scheme') {
if (preg_match('~^https?://~i', $args['content'])) { if (preg_match('~^https?://~i', $args['content'])) {
/** @var resource $fp */
$fp = $this->get_remote_contents($args['content'], 30, 5, 'Mozilla/5.0', $volume->tmpfile()); $fp = $this->get_remote_contents($args['content'], 30, 5, 'Mozilla/5.0', $volume->tmpfile());
if (!$fp) { if (!$fp) {
return array('error' => self::ERROR_SAVE, $args['content'], self::ERROR_FILE_NOT_FOUND); return array('error' => self::ERROR_SAVE, $args['content'], self::ERROR_FILE_NOT_FOUND);
@ -3886,7 +3748,6 @@ class elFinder
{ {
$files = array(); $files = array();
$compare = null; $compare = null;
// long polling mode
if ($args['compare'] && count($args['targets']) === 1) { if ($args['compare'] && count($args['targets']) === 1) {
$compare = intval($args['compare']); $compare = intval($args['compare']);
$hash = $args['targets'][0]; $hash = $args['targets'][0];
@ -4084,7 +3945,6 @@ var go = function() {
} }
} }
} catch(e) { } catch(e) {
// for CORS
w.postMessage && w.postMessage(JSON.stringify({bind:\'' . $bind . '\',data:' . $json . '}), \'' . $origin . '\'); w.postMessage && w.postMessage(JSON.stringify({bind:\'' . $bind . '\',data:' . $json . '}), \'' . $origin . '\');
} }
close(); close();
@ -4206,9 +4066,6 @@ var go = function() {
return $proc; return $proc;
} }
/***************************************************************************/
/* utils */
/***************************************************************************/
/** /**
* Return root - file's owner * Return root - file's owner
@ -4497,9 +4354,6 @@ var go = function() {
return utf8_encode($str); return utf8_encode($str);
} }
/***************************************************************************/
/* static utils */
/***************************************************************************/
/** /**
* Return full version of API that this connector supports all functions * Return full version of API that this connector supports all functions
@ -4925,11 +4779,9 @@ var go = function() {
fputs($stream, join("\r\n", $sends) . "\r\n"); fputs($stream, join("\r\n", $sends) . "\r\n");
while (($res = trim(fgets($stream))) !== '') { while (($res = trim(fgets($stream))) !== '') {
// find redirect
if (preg_match('/^Location: (.+)$/i', $res, $m)) { if (preg_match('/^Location: (.+)$/i', $res, $m)) {
$data['url'] = $m[1]; $data['url'] = $m[1];
} }
// fetch cookie
if (strpos($res, 'Set-Cookie:') === 0) { if (strpos($res, 'Set-Cookie:') === 0) {
$domain = $url['host']; $domain = $url['host'];
if (preg_match('/^Set-Cookie:(.+)(?:domain=\s*([^ ;]+))?/i', $res, $c1)) { if (preg_match('/^Set-Cookie:(.+)(?:domain=\s*([^ ;]+))?/i', $res, $c1)) {
@ -4941,7 +4793,6 @@ var go = function() {
} }
} }
} }
// is seekable url
if (preg_match('/^(Accept-Ranges|Content-Range): bytes/i', $res)) { if (preg_match('/^(Accept-Ranges|Content-Range): bytes/i', $res)) {
elFinder::$seekableUrlFps[(int)$stream] = true; elFinder::$seekableUrlFps[(int)$stream] = true;
} }
@ -5031,7 +4882,6 @@ var go = function() {
if ($file = self::$abortCheckFile) { if ($file = self::$abortCheckFile) {
(version_compare(PHP_VERSION, '5.3.0') >= 0) ? clearstatcache(true, $file) : clearstatcache(); (version_compare(PHP_VERSION, '5.3.0') >= 0) ? clearstatcache(true, $file) : clearstatcache();
if (!is_file($file)) { if (!is_file($file)) {
// GC (expire 12h)
list($ptn) = explode('elfreq', $file); list($ptn) = explode('elfreq', $file);
self::GlobGC($ptn . 'elfreq*', 43200); self::GlobGC($ptn . 'elfreq*', 43200);
return true; return true;
@ -5155,10 +5005,8 @@ var go = function() {
$except = null; $except = null;
$ret = stream_select($read, $write, $except, 1); $ret = stream_select($read, $write, $except, 1);
if ($ret === false) { if ($ret === false) {
// error
break; break;
} else if ($ret === 0) { } else if ($ret === 0) {
// timeout
continue; continue;
} else { } else {
foreach ($read as $sock) { foreach ($read as $sock) {
@ -5186,9 +5034,6 @@ var go = function() {
} }
/***************************************************************************/
/* callbacks */
/***************************************************************************/
/** /**
* Get command name of binded "commandName.subName" * Get command name of binded "commandName.subName"
@ -5270,4 +5115,4 @@ class elFinderAbortException extends Exception
class elFinderTriggerException extends Exception class elFinderTriggerException extends Exception
{ {
} }

View File

@ -74,7 +74,6 @@ class elFinderConnector
$src = $isPost ? array_merge($_GET, $_POST) : $_GET; $src = $isPost ? array_merge($_GET, $_POST) : $_GET;
$maxInputVars = (!$src || isset($src['targets'])) ? ini_get('max_input_vars') : null; $maxInputVars = (!$src || isset($src['targets'])) ? ini_get('max_input_vars') : null;
if ((!$src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) { if ((!$src || $maxInputVars) && $rawPostData = file_get_contents('php://input')) {
// for max_input_vars and supports IE XDomainRequest()
$parts = explode('&', $rawPostData); $parts = explode('&', $rawPostData);
if (!$src || $maxInputVars < count($parts)) { if (!$src || $maxInputVars < count($parts)) {
$src = array(); $src = array();
@ -117,17 +116,14 @@ class elFinderConnector
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_VOL), 'debug' => $this->elFinder->mountErrors)); $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) { if (!$cmd && $isPost) {
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UPLOAD, elFinder::ERROR_UPLOAD_TOTAL_SIZE), 'header' => 'Content-Type: text/html')); $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)) { if (!$this->elFinder->commandExists($cmd)) {
$this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD))); $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_UNKNOWN_CMD)));
} }
// collect required arguments to exec command
$hasFiles = false; $hasFiles = false;
foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) { foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) {
if ($name === 'FILES') { if ($name === 'FILES') {
@ -159,12 +155,8 @@ class elFinderConnector
try { try {
$this->output($this->elFinder->exec($cmd, $args)); $this->output($this->elFinder->exec($cmd, $args));
} catch (elFinderAbortException $e) { } catch (elFinderAbortException $e) {
// connection aborted
// unlock session data for multiple access
$this->elFinder->getSession()->close(); $this->elFinder->getSession()->close();
// HTTP response code
header('HTTP/1.0 204 No Content'); header('HTTP/1.0 204 No Content');
// clear output buffer
while (ob_get_level() && ob_end_clean()) { while (ob_get_level() && ob_end_clean()) {
} }
exit(); exit();
@ -192,9 +184,7 @@ class elFinderConnector
*/ */
protected function output(array $data) protected function output(array $data)
{ {
// unlock session data for multiple access
$this->elFinder->getSession()->close(); $this->elFinder->getSession()->close();
// client disconnect should abort
ignore_user_abort(false); ignore_user_abort(false);
if ($this->header) { if ($this->header) {
@ -202,15 +192,12 @@ class elFinderConnector
} }
if (isset($data['pointer'])) { if (isset($data['pointer'])) {
// set time limit to 0
elFinder::extendTimeLimit(0); elFinder::extendTimeLimit(0);
// send optional header
if (!empty($data['header'])) { if (!empty($data['header'])) {
self::sendHeader($data['header']); self::sendHeader($data['header']);
} }
// clear output buffer
while (ob_get_level() && ob_end_clean()) { while (ob_get_level() && ob_end_clean()) {
} }
@ -245,7 +232,6 @@ class elFinderConnector
header('Content-Length: ' . $psize); header('Content-Length: ' . $psize);
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size); 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 (isset($data['info']['xsendfile']) && strtolower($data['info']['xsendfile']) === 'x-sendfile') {
if (function_exists('header_remove')) { if (function_exists('header_remove')) {
header_remove($data['info']['xsendfile']); header_remove($data['info']['xsendfile']);
@ -278,7 +264,6 @@ class elFinderConnector
if ($sendData) { if ($sendData) {
if ($toEnd || elFinder::isSeekableUrl($fp)) { if ($toEnd || elFinder::isSeekableUrl($fp)) {
// PHP < 5.6 has a bug of fpassthru
// see https://bugs.php.net/bug.php?id=66736 // see https://bugs.php.net/bug.php?id=66736
if (version_compare(PHP_VERSION, '5.6', '<')) { if (version_compare(PHP_VERSION, '5.6', '<')) {
file_put_contents('php://output', $fp); file_put_contents('php://output', $fp);
@ -352,7 +337,6 @@ class elFinderConnector
*/ */
public static function outputJson($data) public static function outputJson($data)
{ {
// send header
$header = isset($data['header']) ? $data['header'] : self::$contentType; $header = isset($data['header']) ? $data['header'] : self::$contentType;
self::sendHeader($header); self::sendHeader($header);
@ -367,7 +351,6 @@ class elFinderConnector
$out = json_encode($data); $out = json_encode($data);
} }
// clear output buffer
while (ob_get_level() && ob_end_clean()) { while (ob_get_level() && ob_end_clean()) {
} }

View File

@ -53,7 +53,6 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver
if ($this->options['icon'] === true) { if ($this->options['icon'] === true) {
unset($this->options['icon']); unset($this->options['icon']);
} }
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
} }
return $res; return $res;
@ -316,7 +315,6 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver
$service = new \Google_Service_Drive($client); $service = new \Google_Service_Drive($client);
// If path is not set, use the root
if (!isset($opts['path']) || $opts['path'] === '') { if (!isset($opts['path']) || $opts['path'] === '') {
$opts['path'] = 'root'; $opts['path'] = 'root';
} }
@ -344,7 +342,6 @@ class elFinderVolumeFlysystemGoogleDriveNetmount extends ExtDriver
} }
if ($res = parent::mount($opts)) { if ($res = parent::mount($opts)) {
// update access_token of session data
if ($creds) { if ($creds) {
$netVolumes = $this->session->get('netvolume'); $netVolumes = $this->session->get('netvolume');
$netVolumes[$this->netMountKey]['access_token'] = array_merge($netVolumes[$this->netMountKey]['access_token'], $creds); $netVolumes[$this->netMountKey]['access_token'] = array_merge($netVolumes[$this->netMountKey]['access_token'], $creds);

View File

@ -51,7 +51,6 @@ class elFinderPlugin
return false; return false;
} }
// check post var 'contentSaveId' to disable this plugin
if ($elfinder && !empty($opts['disableWithContentSaveId'])) { if ($elfinder && !empty($opts['disableWithContentSaveId'])) {
$session = $elfinder->getSession(); $session = $elfinder->getSession();
$urlContentSaveIds = $session->get('urlContentSaveIds', array()); $urlContentSaveIds = $session->get('urlContentSaveIds', array());
@ -64,7 +63,6 @@ class elFinderPlugin
} }
if (isset($opts['onDropWith']) && !is_null($opts['onDropWith'])) { 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']) { if (isset($_REQUEST['dropWith']) && $_REQUEST['dropWith']) {
$onDropWith = $opts['onDropWith']; $onDropWith = $opts['onDropWith'];
$action = (int)$_REQUEST['dropWith']; $action = (int)$_REQUEST['dropWith'];
@ -82,7 +80,6 @@ class elFinderPlugin
} }
if (isset($opts['offDropWith']) && !is_null($opts['offDropWith']) && isset($_REQUEST['dropWith'])) { 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']; $offDropWith = $opts['offDropWith'];
$action = (int)$_REQUEST['dropWith']; $action = (int)$_REQUEST['dropWith'];
if (!is_array($offDropWith)) { if (!is_array($offDropWith)) {

View File

@ -126,7 +126,6 @@ class elFinderSession implements elFinderSessionInterface
{ {
set_error_handler(array($this, 'session_start_error'), E_NOTICE | E_WARNING); 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://bugs.php.net/bug.php?id=75554
// see https://github.com/php/php-src/pull/3231 // see https://github.com/php/php-src/pull/3231
if ($this->fixCookieRegist === true) { if ($this->fixCookieRegist === true) {
@ -223,7 +222,6 @@ class elFinderSession implements elFinderSessionInterface
{ {
if ($this->started) { if ($this->started) {
if ($this->fixCookieRegist === true) { if ($this->fixCookieRegist === true) {
// regist cookie only once for apache2 SAPI
$cParm = session_get_cookie_params(); $cParm = session_get_cookie_params();
if ($this->opts['cookieParams'] && is_array($this->opts['cookieParams'])) { if ($this->opts['cookieParams'] && is_array($this->opts['cookieParams'])) {
$cParm = array_merge($cParm, $this->opts['cookieParams']); $cParm = array_merge($cParm, $this->opts['cookieParams']);

View File

@ -114,9 +114,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$this->options['mimeDetect'] = 'internal'; $this->options['mimeDetect'] = 'internal';
} }
/*********************************************************************/
/* ORIGINAL FUNCTIONS */
/*********************************************************************/
/** /**
* Get Parent ID, Item ID, Parent Path as an array from path. * Get Parent ID, Item ID, Parent Path as an array from path.
@ -189,7 +186,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
); );
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $fields, CURLOPT_POSTFIELDS => $fields,
@ -238,7 +234,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
if ($aTokenFile && is_file($aTokenFile)) { if ($aTokenFile && is_file($aTokenFile)) {
$lock = $aTokenFile . '.lock'; $lock = $aTokenFile . '.lock';
if (file_exists($lock)) { if (file_exists($lock)) {
// Probably updating on other instance
return true; return true;
} }
touch($lock); touch($lock);
@ -257,7 +252,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$curl = curl_init(); $curl = curl_init();
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, // i am sending post data CURLOPT_POST => true, // i am sending post data
CURLOPT_POSTFIELDS => http_build_query($postData), CURLOPT_POSTFIELDS => http_build_query($postData),
@ -303,8 +297,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
file_put_contents($aTokenFile, json_encode($token), LOCK_EX); file_put_contents($aTokenFile, json_encode($token), LOCK_EX);
$this->options['accessToken'] = $json; $this->options['accessToken'] = $json;
} else if (!empty($this->options['netkey'])) { } else if (!empty($this->options['netkey'])) {
// OAuth2 refresh token can be used only once,
// so update it if it is the same as the token file
if ($aTokenFile && is_file($aTokenFile)) { if ($aTokenFile && is_file($aTokenFile)) {
if ($_token = json_decode(file_get_contents($aTokenFile))) { if ($_token = json_decode(file_get_contents($aTokenFile))) {
if ($_token->data->refresh_token === $refresh_token) { if ($_token->data->refresh_token === $refresh_token) {
@ -313,7 +305,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
} }
$this->options['accessToken'] = $json; $this->options['accessToken'] = $json;
// update session value
elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $json); elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $json);
$this->session->set('BoxTokens', $token); $this->session->set('BoxTokens', $token);
} else { } else {
@ -338,7 +329,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$curl = curl_init(); $curl = curl_init();
$defaultOptions = array( $defaultOptions = array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
); );
@ -436,7 +426,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
throw new \Exception($errmsg); throw new \Exception($errmsg);
} }
// make catch
if ($decodeOrParent && $decodeOrParent !== true) { if ($decodeOrParent && $decodeOrParent !== true) {
$raws = null; $raws = null;
if (isset($decoded->entries)) { if (isset($decoded->entries)) {
@ -610,7 +599,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_CUSTOMREQUEST => 'DELETE',
)); ));
//unlink or delete File or Folder in the Parent
$this->_bd_curlExec($curl); $this->_bd_curlExec($curl);
} catch (Exception $e) { } catch (Exception $e) {
return $this->setError('Box error: ' . $e->getMessage()); return $this->setError('Box error: ' . $e->getMessage());
@ -652,9 +640,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken); return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken);
} }
/*********************************************************************/
/* OVERRIDE FUNCTIONS */
/*********************************************************************/
/** /**
* Prepare * Prepare
@ -702,7 +687,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
if ($code) { if ($code) {
try { try {
if (!empty($options['id'])) { if (!empty($options['id'])) {
// Obtain the token using the code received by the Box.com API
$this->session->set('BoxTokens', $this->session->set('BoxTokens',
$this->_bd_obtainAccessToken($options['client_id'], $options['client_secret'], $code)); $this->_bd_obtainAccessToken($options['client_id'], $options['client_secret'], $code));
@ -876,9 +860,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare FTP connection * Prepare FTP connection
@ -908,7 +889,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
throw new Exception('Required option `accessToken` is invalid JSON.'); throw new Exception('Required option `accessToken` is invalid JSON.');
} }
// make net mount key
if (empty($this->options['netkey'])) { if (empty($this->options['netkey'])) {
$this->netMountKey = $this->_bd_getInitialToken(); $this->netMountKey = $this->_bd_getInitialToken();
} else { } else {
@ -929,7 +909,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
} }
} else if (is_file($this->aTokenFile)) { } else if (is_file($this->aTokenFile)) {
// If the refresh token is the same as the permanent volume
$this->token = json_decode(file_get_contents($this->aTokenFile)); $this->token = json_decode(file_get_contents($this->aTokenFile));
} }
} }
@ -947,13 +926,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver
if ($error) { if ($error) {
if (empty($this->options['netkey']) && $this->tmbPrefix) { if (empty($this->options['netkey']) && $this->tmbPrefix) {
// for delete thumbnail
$this->netunmount(null, null); $this->netunmount(null, null);
} }
return false; return false;
} }
// normalize root path
if ($this->options['path'] == 'root') { if ($this->options['path'] == 'root') {
$this->options['path'] = '/'; $this->options['path'] = '/';
} }
@ -977,13 +954,10 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$this->rootName = $this->options['alias']; $this->rootName = $this->options['alias'];
// This driver dose not support `syncChkAsTs`
$this->options['syncChkAsTs'] = false; $this->options['syncChkAsTs'] = false;
// 'lsPlSleep' minmum 10 sec
$this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']); $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
return true; return true;
@ -999,15 +973,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver
{ {
parent::configure(); parent::configure();
// fallback of $this->tmp
if (!$this->tmp && $this->tmbPathWritable) { if (!$this->tmp && $this->tmbPathWritable) {
$this->tmp = $this->tmbPath; $this->tmp = $this->tmbPath;
} }
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection. * Close opened connection.
@ -1032,9 +1002,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver
{ {
list(, $name, $parent) = $this->_bd_splitPath($path); list(, $name, $parent) = $this->_bd_splitPath($path);
// We can not use it because the search of Box.com there is a time lag.
// ref. https://docs.box.com/reference#searching-for-content // ref. https://docs.box.com/reference#searching-for-content
// > Note: If an item is added to Box then it becomes accessible through the search endpoint after ten minutes.
/*** /***
* $url = self::API_URL.'/search?limit=1&offset=0&content_types=name&ancestor_folder_ids='.rawurlencode($pid) * $url = self::API_URL.'/search?limit=1&offset=0&content_types=name&ancestor_folder_ids='.rawurlencode($pid)
@ -1048,7 +1016,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$phash = $this->encode($parent); $phash = $this->encode($parent);
// do not recursive search
$searchExDirReg = $this->options['searchExDirReg']; $searchExDirReg = $this->options['searchExDirReg'];
$this->options['searchExDirReg'] = '/.*/'; $this->options['searchExDirReg'] = '/.*/';
$search = $this->search($name, array(), $phash); $search = $this->search($name, array(), $phash);
@ -1199,9 +1166,7 @@ class elFinderVolumeBox extends elFinderVolumeDriver
$name = $this->tmbname($stat); $name = $this->tmbname($stat);
$tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
// copy image into tmbPath so some drivers does not store files on local fs
if (!$data = $this->_bd_getThumbnail($path)) { if (!$data = $this->_bd_getThumbnail($path)) {
// try get full contents as fallback
if (!$data = $this->_getContents($path)) { if (!$data = $this->_getContents($path)) {
return false; return false;
} }
@ -1217,13 +1182,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
$result = true; $result = true;
/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
} else { } else {
if ($this->options['tmbCrop']) { if ($this->options['tmbCrop']) {
/* Resize and crop if image bigger than thumbnail */
if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
} }
@ -1299,7 +1262,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return parent::getContentUrl($hash, $options); return parent::getContentUrl($hash, $options);
} }
if (!empty($options['temporary'])) { if (!empty($options['temporary'])) {
// try make temporary file
$url = parent::getContentUrl($hash, $options); $url = parent::getContentUrl($hash, $options);
if ($url) { if ($url) {
return $url; return $url;
@ -1319,7 +1281,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
CURLOPT_POSTFIELDS => json_encode($params), CURLOPT_POSTFIELDS => json_encode($params),
)); ));
$res = $this->_bd_curlExec($curl, true, array( $res = $this->_bd_curlExec($curl, true, array(
// The data is sent as JSON as per Box documentation.
'Content-Type: application/json', 'Content-Type: application/json',
)); ));
@ -1331,7 +1292,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return ''; return '';
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path. * Return parent directory path.
@ -1452,7 +1412,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return $path == $parent || strpos($path, $parent . '/') === 0; return $path == $parent || strpos($path, $parent . '/') === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -1544,7 +1503,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1581,7 +1539,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
'headers' => array('Authorization: Bearer ' . $this->token->data->access_token), 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token),
); );
// to support range request
if (func_num_args() > 2) { if (func_num_args() > 2) {
$opts = func_get_arg(2); $opts = func_get_arg(2);
} else { } else {
@ -1614,7 +1571,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed. * Create dir and return created dir path or false on failed.
@ -1639,7 +1595,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
CURLOPT_POSTFIELDS => json_encode($params), CURLOPT_POSTFIELDS => json_encode($params),
)); ));
//create the Folder in the Parent
$folder = $this->_bd_curlExec($curl, $path); $folder = $this->_bd_curlExec($curl, $path);
return $this->_joinPath($path, $folder->id); return $this->_joinPath($path, $folder->id);
@ -1689,7 +1644,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
protected function _copy($source, $targetDir, $name) protected function _copy($source, $targetDir, $name)
{ {
try { try {
//Set the Parent id
list(, $parentId) = $this->_bd_splitPath($targetDir); list(, $parentId) = $this->_bd_splitPath($targetDir);
list(, $srcId) = $this->_bd_splitPath($source); list(, $srcId) = $this->_bd_splitPath($source);
@ -1707,7 +1661,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_POSTFIELDS => json_encode($data),
)); ));
//copy File in the Parent
$result = $this->_bd_curlExec($curl, $targetDir); $result = $this->_bd_curlExec($curl, $targetDir);
if (isset($result->id)) { if (isset($result->id)) {
@ -1738,14 +1691,11 @@ class elFinderVolumeBox extends elFinderVolumeDriver
protected function _move($source, $targetDir, $name) protected function _move($source, $targetDir, $name)
{ {
try { try {
//moving and renaming a file or directory
//Set new Parent and remove old parent
list(, $parentId) = $this->_bd_splitPath($targetDir); list(, $parentId) = $this->_bd_splitPath($targetDir);
list(, $itemId) = $this->_bd_splitPath($source); list(, $itemId) = $this->_bd_splitPath($source);
$srcItem = $this->_bd_getRawItem($source); $srcItem = $this->_bd_getRawItem($source);
//rename or move file or folder in destination target
$properties = array('name' => $name, 'parent' => array('id' => $parentId)); $properties = array('name' => $name, 'parent' => array('id' => $parentId));
$type = ($srcItem->type === 'folder') ? 'folders' : 'files'; $type = ($srcItem->type === 'folder') ? 'folders' : 'files';
@ -1759,7 +1709,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
)); ));
$result = $this->_bd_curlExec($curl, $targetDir, array( $result = $this->_bd_curlExec($curl, $targetDir, array(
// The data is sent as JSON as per Box documentation.
'Content-Type: application/json', 'Content-Type: application/json',
)); ));
@ -1830,10 +1779,8 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
try { try {
//Create or Update a file
$metaDatas = stream_get_meta_data($fp); $metaDatas = stream_get_meta_data($fp);
$tmpFilePath = isset($metaDatas['uri']) ? $metaDatas['uri'] : ''; $tmpFilePath = isset($metaDatas['uri']) ? $metaDatas['uri'] : '';
// remote contents
if (!$tmpFilePath || empty($metaDatas['seekable'])) { if (!$tmpFilePath || empty($metaDatas['seekable'])) {
$tmpHandle = $this->tmpfile(); $tmpHandle = $this->tmpfile();
stream_copy_to_stream($fp, $tmpHandle); stream_copy_to_stream($fp, $tmpHandle);
@ -1842,11 +1789,9 @@ class elFinderVolumeBox extends elFinderVolumeDriver
} }
if ($itemId === '') { if ($itemId === '') {
//upload or create new file in destination target
$properties = array('name' => $name, 'parent' => array('id' => $parentId)); $properties = array('name' => $name, 'parent' => array('id' => $parentId));
$url = self::UPLOAD_URL . '/files/content'; $url = self::UPLOAD_URL . '/files/content';
} else { } else {
//update existing file in destination target
$properties = array('name' => $name); $properties = array('name' => $name);
$url = self::UPLOAD_URL . '/files/' . $itemId . '/content'; $url = self::UPLOAD_URL . '/files/' . $itemId . '/content';
} }
@ -1924,7 +1869,6 @@ class elFinderVolumeBox extends elFinderVolumeDriver
**/ **/
protected function _checkArchivers() protected function _checkArchivers()
{ {
// die('Not yet implemented. (_checkArchivers)');
return array(); return array();
} }

View File

@ -85,11 +85,9 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
*/ */
public function __construct() { public function __construct() {
// check with composer
$this->dropbox_phpFound = class_exists('Dropbox_API'); $this->dropbox_phpFound = class_exists('Dropbox_API');
if (! $this->dropbox_phpFound) { if (! $this->dropbox_phpFound) {
// check with pear
if (include_once 'Dropbox/autoload.php') { if (include_once 'Dropbox/autoload.php') {
$this->dropbox_phpFound = in_array('Dropbox_autoload', spl_autoload_functions()); $this->dropbox_phpFound = in_array('Dropbox_autoload', spl_autoload_functions());
} }
@ -148,7 +146,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
$this->oauth = new Dropbox_OAuth_PHP($options['consumerKey'], $options['consumerSecret']); $this->oauth = new Dropbox_OAuth_PHP($options['consumerKey'], $options['consumerSecret']);
} else { } else {
if (! class_exists('HTTP_OAuth_Consumer')) { if (! class_exists('HTTP_OAuth_Consumer')) {
// We're going to try to load in manually
include 'HTTP/OAuth/Consumer.php'; include 'HTTP/OAuth/Consumer.php';
} }
if (class_exists('HTTP_OAuth_Consumer', false)) { if (class_exists('HTTP_OAuth_Consumer', false)) {
@ -164,7 +161,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
if ($options['pass'] === 'init') { if ($options['pass'] === 'init') {
$html = ''; $html = '';
if ($sessionToken = $this->session->get('DropboxTokens')) { if ($sessionToken = $this->session->get('DropboxTokens')) {
// token check
try { try {
list(, $accessToken, $accessTokenSecret) = $sessionToken; list(, $accessToken, $accessTokenSecret) = $sessionToken;
$this->oauth->setToken($accessToken, $accessTokenSecret); $this->oauth->setToken($accessToken, $accessTokenSecret);
@ -179,7 +175,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
} }
} }
if (! $html) { if (! $html) {
// get customdata
$cdata = ''; $cdata = '';
$innerKeys = array('cmd', 'host', 'options', 'pass', 'protocol', 'user'); $innerKeys = array('cmd', 'host', 'options', 'pass', 'protocol', 'user');
$this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET; $this->ARGS = $_SERVER['REQUEST_METHOD'] === 'POST'? $_POST : $_GET;
@ -259,9 +254,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return true; return true;
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare FTP connection * Prepare FTP connection
@ -287,7 +279,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
$this->options['metaCachePath'] = ELFINDER_DROPBOX_META_CACHE_PATH; $this->options['metaCachePath'] = ELFINDER_DROPBOX_META_CACHE_PATH;
} }
// make net mount key
$this->netMountKey = md5(join('-', array('dropbox', $this->options['path']))); $this->netMountKey = md5(join('-', array('dropbox', $this->options['path'])));
if (! $this->oauth) { if (! $this->oauth) {
@ -298,7 +289,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
$this->oauth = new Dropbox_OAuth_PHP($this->options['consumerKey'], $this->options['consumerSecret']); $this->oauth = new Dropbox_OAuth_PHP($this->options['consumerKey'], $this->options['consumerSecret']);
} else { } else {
if (! class_exists('HTTP_OAuth_Consumer')) { if (! class_exists('HTTP_OAuth_Consumer')) {
// We're going to try to load in manually
include 'HTTP/OAuth/Consumer.php'; include 'HTTP/OAuth/Consumer.php';
} }
if (class_exists('HTTP_OAuth_Consumer', false)) { if (class_exists('HTTP_OAuth_Consumer', false)) {
@ -312,7 +302,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $this->setError('OAuth extension not loaded.'); return $this->setError('OAuth extension not loaded.');
} }
// normalize root path
$this->root = $this->options['path'] = $this->_normpath($this->options['path']); $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
if (empty($this->options['alias'])) { if (empty($this->options['alias'])) {
@ -329,7 +318,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $this->setError('Dropbox error: '.$e->getMessage()); return $this->setError('Dropbox error: '.$e->getMessage());
} }
// user
if (empty($this->options['dropboxUid'])) { if (empty($this->options['dropboxUid'])) {
try { try {
$res = $this->dropbox->getAccountInfo(); $res = $this->dropbox->getAccountInfo();
@ -368,13 +356,10 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $this->setError('Cache dirctory (metaCachePath or tmp) is require.'); return $this->setError('Cache dirctory (metaCachePath or tmp) is require.');
} }
// setup PDO
if (! $this->options['PDO_DSN']) { if (! $this->options['PDO_DSN']) {
$this->options['PDO_DSN'] = 'sqlite:'.$this->metaCache.DIRECTORY_SEPARATOR.'.elFinder_dropbox_db_'.md5($this->dropboxUid.$this->options['consumerSecret']); $this->options['PDO_DSN'] = 'sqlite:'.$this->metaCache.DIRECTORY_SEPARATOR.'.elFinder_dropbox_db_'.md5($this->dropboxUid.$this->options['consumerSecret']);
} }
// DataBase table name
$this->DB_TableName = $this->options['PDO_DBName']; $this->DB_TableName = $this->options['PDO_DBName'];
// DataBase check or make table
try { try {
$this->DB = new PDO($this->options['PDO_DSN'], $this->options['PDO_User'], $this->options['PDO_Pass'], $this->options['PDO_Options']); $this->DB = new PDO($this->options['PDO_DSN'], $this->options['PDO_User'], $this->options['PDO_Pass'], $this->options['PDO_Options']);
if (! $this->checkDB()) { if (! $this->checkDB()) {
@ -397,10 +382,8 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
$this->options['syncChkAsTs'] = true; $this->options['syncChkAsTs'] = true;
} }
if ($this->options['syncChkAsTs']) { if ($this->options['syncChkAsTs']) {
// 'tsPlSleep' minmum 5 sec
$this->options['tsPlSleep'] = max(5, $this->options['tsPlSleep']); $this->options['tsPlSleep'] = max(5, $this->options['tsPlSleep']);
} else { } else {
// 'lsPlSleep' minmum 10 sec
$this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']); $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
} }
@ -429,10 +412,8 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
private function checkDB() { private function checkDB() {
$res = $this->query('SELECT * FROM sqlite_master WHERE type=\'table\' AND name=\''.$this->DB_TableName.'\''); $res = $this->query('SELECT * FROM sqlite_master WHERE type=\'table\' AND name=\''.$this->DB_TableName.'\'');
if ($res && isset($_REQUEST['init'])) { if ($res && isset($_REQUEST['init'])) {
// check is index(nameidx) UNIQUE?
$chk = $this->query('SELECT sql FROM sqlite_master WHERE type=\'index\' and name=\'nameidx\''); $chk = $this->query('SELECT sql FROM sqlite_master WHERE type=\'index\' and name=\'nameidx\'');
if (!$chk || strpos(strtoupper($chk[0]), 'UNIQUE') === false) { if (!$chk || strpos(strtoupper($chk[0]), 'UNIQUE') === false) {
// remake
$this->DB->exec('DROP TABLE '.$this->DB_TableName); $this->DB->exec('DROP TABLE '.$this->DB_TableName);
$res = false; $res = false;
} }
@ -490,9 +471,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
. ', isdir=' . ($dat['is_dir']? 1 : 0) . ', isdir=' . ($dat['is_dir']? 1 : 0)
. ' where path='.$this->DB->quote(strtolower($this->_dirname($path))).' and fname='.$this->DB->quote(strtolower($this->_basename($path)))); . ' where path='.$this->DB->quote(strtolower($this->_dirname($path))).' and fname='.$this->DB->quote(strtolower($this->_basename($path))));
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection * Close opened connection
@ -575,7 +553,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
} }
} while (! empty($_info['has_more'])); } while (! empty($_info['has_more']));
// update time stamp of parent holder
foreach ($ptimes as $_p => $_t) { foreach ($ptimes as $_p => $_t) {
if ($praw = $this->getDBdat($_p)) { if ($praw = $this->getDBdat($_p)) {
$_update = false; $_update = false;
@ -791,7 +768,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
$name = $this->tmbname($stat); $name = $this->tmbname($stat);
$tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name; $tmb = $this->tmbPath.DIRECTORY_SEPARATOR.$name;
// copy image into tmbPath so some drivers does not store files on local fs
if (! $data = $this->getThumbnail($path, $this->options['getTmbSize'])) { if (! $data = $this->getThumbnail($path, $this->options['getTmbSize'])) {
return false; return false;
} }
@ -807,7 +783,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return false; return false;
} }
/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' ); $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png' );
@ -815,7 +790,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
if ($this->options['tmbCrop']) { if ($this->options['tmbCrop']) {
/* Resize and crop if image bigger than thumbnail */
if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize) ) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
} }
@ -958,7 +932,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $res; return $res;
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path * Return parent directory path
@ -1051,7 +1024,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $path == $parent || strpos($path, $parent.'/') === 0; return $path == $parent || strpos($path, $parent.'/') === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -1072,7 +1044,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
* @author Dmitry (dio) Levashov * @author Dmitry (dio) Levashov
**/ **/
protected function _stat($path) { protected function _stat($path) {
//if (!empty($this->ARGS['reload']) && isset($this->ARGS['target']) && strpos($this->ARGS['target'], $this->id) === 0) {
if ($this->isMyReload()) { if ($this->isMyReload()) {
$this->deltaCheck(); $this->deltaCheck();
} }
@ -1121,7 +1092,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1195,7 +1165,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed * Create dir and return created dir path or false on failed
@ -1392,7 +1361,6 @@ class elFinderVolumeDropbox extends elFinderVolumeDriver {
* @return array * @return array
**/ **/
protected function _checkArchivers() { protected function _checkArchivers() {
// die('Not yet implemented. (_checkArchivers)');
return array(); return array();
} }

View File

@ -72,8 +72,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
'rootCssClass' => 'elfinder-navbar-root-dropbox', 'rootCssClass' => 'elfinder-navbar-root-dropbox',
'publishPermission' => [ 'publishPermission' => [
'requested_visibility' => 'public', 'requested_visibility' => 'public',
//'link_password' => '',
//'expires' => '',
], ],
'getThumbSize' => 'medium', // Available sizes: 'thumb', 'small', 'medium', 'large', 'huge' 'getThumbSize' => 'medium', // Available sizes: 'thumb', 'small', 'medium', 'large', 'huge'
]; ];
@ -81,9 +79,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$this->options['mimeDetect'] = 'internal'; $this->options['mimeDetect'] = 'internal';
} }
/*********************************************************************/
/* ORIGINAL FUNCTIONS */
/*********************************************************************/
/** /**
* Get Parent ID, Item ID, Parent Path as an array from path. * Get Parent ID, Item ID, Parent Path as an array from path.
@ -151,7 +146,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$stat = []; $stat = [];
$isFolder = false; $isFolder = false;
if ($raw === true) { if ($raw === true) {
// root folder
$isFolder = true; $isFolder = true;
$stat['name'] = ''; $stat['name'] = '';
$stat['iid'] = '0'; $stat['iid'] = '0';
@ -262,9 +256,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return isset($res['oauth2_token']) ? $res['oauth2_token'] : false; return isset($res['oauth2_token']) ? $res['oauth2_token'] : false;
} }
/*********************************************************************/
/* EXTENDED FUNCTIONS */
/*********************************************************************/
/** /**
* Prepare * Prepare
@ -483,9 +474,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return true; return true;
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare Dropbox connection * Prepare Dropbox connection
@ -518,7 +506,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
} }
try { try {
// make net mount key for network mount
$aToken = $this->options['access_token']; $aToken = $this->options['access_token'];
$this->netMountKey = md5($aToken . '-' . $this->options['path']); $this->netMountKey = md5($aToken . '-' . $this->options['path']);
@ -526,7 +513,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
if ($this->needOnline && !$this->service) { if ($this->needOnline && !$this->service) {
$app = new DropboxApp($this->options['app_key'], $this->options['app_secret'], $aToken); $app = new DropboxApp($this->options['app_key'], $this->options['app_secret'], $aToken);
$this->service = new Dropbox($app); $this->service = new Dropbox($app);
// to check access_token
$this->service->getCurrentAccount(); $this->service->getCurrentAccount();
} }
} catch (DropboxClientException $e) { } catch (DropboxClientException $e) {
@ -543,7 +529,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return $this->setError($errors); return $this->setError($errors);
} }
// normalize root path
$this->options['path'] = strtolower($this->options['path']); $this->options['path'] = strtolower($this->options['path']);
if ($this->options['path'] == 'root') { if ($this->options['path'] == 'root') {
$this->options['path'] = '/'; $this->options['path'] = '/';
@ -569,13 +554,10 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$this->tmp = $tmp; $this->tmp = $tmp;
} }
// This driver dose not support `syncChkAsTs`
$this->options['syncChkAsTs'] = false; $this->options['syncChkAsTs'] = false;
// 'lsPlSleep' minmum 10 sec
$this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']); $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
return true; return true;
@ -591,19 +573,14 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
{ {
parent::configure(); parent::configure();
// fallback of $this->tmp
if (!$this->tmp && $this->tmbPathWritable) { if (!$this->tmp && $this->tmbPathWritable) {
$this->tmp = $this->tmbPath; $this->tmp = $this->tmbPath;
} }
if ($this->isMyReload()) { if ($this->isMyReload()) {
//$this->_db_getDirectoryData(false);
} }
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection. * Close opened connection.
@ -664,7 +641,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
protected function doSearch($path, $q, $mimes) protected function doSearch($path, $q, $mimes)
{ {
if (!empty($this->doSearchCurrentQuery['matchMethod']) || $mimes) { if (!empty($this->doSearchCurrentQuery['matchMethod']) || $mimes) {
// has custom match method or mimes, use elFinderVolumeDriver::doSearch()
return parent::doSearch($path, $q, $mimes); return parent::doSearch($path, $q, $mimes);
} }
@ -790,7 +766,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$name = $this->tmbname($stat); $name = $this->tmbname($stat);
$tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
// copy image into tmbPath so some drivers does not store files on local fs
if (!$data = $this->_db_getThumbnail($path)) { if (!$data = $this->_db_getThumbnail($path)) {
return false; return false;
} }
@ -806,13 +781,11 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$result = true; $result = true;
/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
} else { } else {
if ($this->options['tmbCrop']) { if ($this->options['tmbCrop']) {
/* Resize and crop if image bigger than thumbnail */
if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
} }
@ -875,7 +848,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return parent::getContentUrl($hash, $options); return parent::getContentUrl($hash, $options);
} }
if (!empty($options['temporary'])) { if (!empty($options['temporary'])) {
// try make temporary file
$url = parent::getContentUrl($hash, $options); $url = parent::getContentUrl($hash, $options);
if ($url) { if ($url) {
return $url; return $url;
@ -933,7 +905,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path. * Return parent directory path.
@ -1055,7 +1026,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return $path == $parent || strpos($path, $parent . '/') === 0; return $path == $parent || strpos($path, $parent . '/') === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -1150,7 +1120,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1187,7 +1156,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
'headers' => array('Authorization: Bearer ' . $access_token), 'headers' => array('Authorization: Bearer ' . $access_token),
); );
// to support range request
if (func_num_args() > 2) { if (func_num_args() > 2) {
$opts = func_get_arg(2); $opts = func_get_arg(2);
} else { } else {
@ -1218,7 +1186,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
is_resource($fp) && fclose($fp); is_resource($fp) && fclose($fp);
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed. * Create dir and return created dir path or false on failed.
@ -1421,7 +1388,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
$name = ''; $name = '';
$stat = $this->stat($path); $stat = $this->stat($path);
if ($stat) { if ($stat) {
// keep real name
$path = $this->_dirname($path); $path = $this->_dirname($path);
$name = $stat['name']; $name = $stat['name'];
} }
@ -1439,7 +1405,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
**/ **/
protected function _checkArchivers() protected function _checkArchivers()
{ {
// die('Not yet implemented. (_checkArchivers)');
return []; return [];
} }
@ -1466,7 +1431,6 @@ class elFinderVolumeDropbox2 extends elFinderVolumeDriver
protected function _unpack($path, $arc) protected function _unpack($path, $arc)
{ {
die('Not yet implemented. (_unpack)'); die('Not yet implemented. (_unpack)');
//return false;
} }
/** /**

View File

@ -149,9 +149,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $options; return $options;
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare FTP connection * Prepare FTP connection
@ -176,21 +173,18 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$this->options['path'] = '/'; $this->options['path'] = '/';
} }
// make ney mount key
$this->netMountKey = md5(join('-', array('ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user']))); $this->netMountKey = md5(join('-', array('ftp', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user'])));
if (!function_exists('ftp_connect')) { if (!function_exists('ftp_connect')) {
return $this->setError('FTP extension not loaded.'); return $this->setError('FTP extension not loaded.');
} }
// remove protocol from host
$scheme = parse_url($this->options['host'], PHP_URL_SCHEME); $scheme = parse_url($this->options['host'], PHP_URL_SCHEME);
if ($scheme) { if ($scheme) {
$this->options['host'] = substr($this->options['host'], strlen($scheme) + 3); $this->options['host'] = substr($this->options['host'], strlen($scheme) + 3);
} }
// normalize root path
$this->root = $this->options['path'] = $this->_normpath($this->options['path']); $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
if (empty($this->options['alias'])) { if (empty($this->options['alias'])) {
@ -236,7 +230,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$this->tmp = $tmp; $this->tmp = $tmp;
} }
// fallback of $this->tmp
if (!$this->tmp && $this->tmbPathWritable) { if (!$this->tmp && $this->tmbPathWritable) {
$this->tmp = $this->tmbPath; $this->tmp = $this->tmbPath;
} }
@ -251,7 +244,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$this->disabled[] = 'extract'; $this->disabled[] = 'extract';
} }
// echo $this->tmp;
} }
@ -279,7 +271,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $this->setError('Unable to login into ' . $this->options['host'] . $withSSL); return $this->setError('Unable to login into ' . $this->options['host'] . $withSSL);
} }
// try switch utf8 mode
if ($this->encoding) { if ($this->encoding) {
ftp_raw($this->connect, 'OPTS UTF8 OFF'); ftp_raw($this->connect, 'OPTS UTF8 OFF');
} else { } else {
@ -290,11 +281,8 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$this->isPureFtpd = stripos(implode(' ', $help), 'Pure-FTPd') !== false; $this->isPureFtpd = stripos(implode(' ', $help), 'Pure-FTPd') !== false;
if (!$this->isPureFtpd) { if (!$this->isPureFtpd) {
// switch off extended passive mode - may be usefull for some servers
// this command, for pure-ftpd, doesn't work and takes a timeout in some pure-ftpd versions
ftp_raw($this->connect, 'epsv4 off'); ftp_raw($this->connect, 'epsv4 off');
} }
// enter passive mode if required
$pasv = ($this->options['mode'] == 'passive'); $pasv = ($this->options['mode'] == 'passive');
if (!ftp_pasv($this->connect, $pasv)) { if (!ftp_pasv($this->connect, $pasv)) {
if ($pasv) { if ($pasv) {
@ -302,14 +290,12 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
} }
// enter root folder
if (!ftp_chdir($this->connect, $this->root) if (!ftp_chdir($this->connect, $this->root)
|| $this->root != ftp_pwd($this->connect)) { || $this->root != ftp_pwd($this->connect)) {
$this->umount(); $this->umount();
return $this->setError('Unable to open root folder.'); return $this->setError('Unable to open root folder.');
} }
// check for MLST support
$features = ftp_raw($this->connect, 'FEAT'); $features = ftp_raw($this->connect, 'FEAT');
if (!is_array($features)) { if (!is_array($features)) {
$this->umount(); $this->umount();
@ -348,9 +334,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection * Close opened connection
@ -405,7 +388,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
if (preg_match('|(.+)\-\>(.+)|', $name, $m)) { if (preg_match('|(.+)\-\>(.+)|', $name, $m)) {
$name = trim($m[1]); $name = trim($m[1]);
// check recursive processing
if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) { if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) {
return array(); return array();
} }
@ -426,7 +408,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
if (is_numeric($info[5]) && !$info[6] && !$info[7]) { if (is_numeric($info[5]) && !$info[6] && !$info[7]) {
// by normalizeRawWindows()
$stat['ts'] = $info[5]; $stat['ts'] = $info[5];
} else { } else {
$stat['ts'] = strtotime($info[5] . ' ' . $info[6] . ' ' . $info[7]); $stat['ts'] = strtotime($info[5] . ' ' . $info[6] . ' ' . $info[7]);
@ -445,12 +426,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$stat['owner'] = $info[2]; $stat['owner'] = $info[2];
$stat['group'] = $info[3]; $stat['group'] = $info[3];
$stat['perm'] = substr($info[0], 1); $stat['perm'] = substr($info[0], 1);
//
// if not exists owner in LS ftp ==> isowner = true
// if is defined as option : 'owner' => true isowner = true
//
// if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp)
//
$stat['isowner'] = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; $stat['isowner'] = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true;
} }
@ -552,7 +527,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
foreach ($list as $stat) { foreach ($list as $stat) {
$p = $prefix . $stat['name']; $p = $prefix . $stat['name'];
if (isset($stat['target'])) { if (isset($stat['target'])) {
// stat later
$targets[$stat['name']] = $stat['target']; $targets[$stat['name']] = $stat['target'];
} else { } else {
$stat = $this->updateCache($p, $stat); $stat = $this->updateCache($p, $stat);
@ -564,7 +538,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
} }
} }
// stat link targets
foreach ($targets as $name => $target) { foreach ($targets as $name => $target) {
$stat = array(); $stat = array();
$stat['name'] = $name; $stat['name'] = $name;
@ -630,7 +603,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY; return strpos($this->mimetype($path), 'text/') === 0 ? FTP_ASCII : FTP_BINARY;
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path * Return parent directory path
@ -688,7 +660,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
if (empty($path)) { if (empty($path)) {
$path = '.'; $path = '.';
} }
// path must be start with /
$path = preg_replace('|^\.\/?|', $this->separator, $path); $path = preg_replace('|^\.\/?|', $this->separator, $path);
$path = preg_replace('/^([^\/])/', "/$1", $path); $path = preg_replace('/^([^\/])/', "/$1", $path);
@ -746,7 +717,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
if (strpos($path, $this->root) === 0) { if (strpos($path, $this->root) === 0) {
return ltrim(substr($path, strlen($this->root)), $this->separator); return ltrim(substr($path, strlen($this->root)), $this->separator);
} else { } else {
// for link
return $path; return $path;
} }
} }
@ -766,7 +736,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $this->root; return $this->root;
} else { } else {
if ($path[0] === $this->separator) { if ($path[0] === $this->separator) {
// for link
return $path; return $path;
} else { } else {
return $this->_joinPath($this->root, $path); return $this->_joinPath($this->root, $path);
@ -801,7 +770,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $path == $parent || strpos($path, rtrim($parent, $this->separator) . $this->separator) === 0; return $path == $parent || strpos($path, rtrim($parent, $this->separator) . $this->separator) === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -893,7 +861,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$stat = $this->convEncIn(isset($this->cache[$outPath]) ? $this->cache[$outPath] : array()); $stat = $this->convEncIn(isset($this->cache[$outPath]) ? $this->cache[$outPath] : array());
if (!$this->mounted) { if (!$this->mounted) {
// dispose incomplete cache made by calling `stat` by 'startPath' option
$this->cache = array(); $this->cache = array();
} }
return $stat; return $stat;
@ -957,7 +924,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return array(); return array();
} }
// do not use MLST to get stat of symlink
if ($stat['mime'] === 'symlink') { if ($stat['mime'] === 'symlink') {
$this->MLSTsupprt = false; $this->MLSTsupprt = false;
$res = $this->_stat($path); $res = $this->_stat($path);
@ -1005,11 +971,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
$stat['perm'] = trim($stat['perm']); $stat['perm'] = trim($stat['perm']);
//
// if not exists owner in LS ftp ==> isowner = true
// if is defined as option : 'owner' => true isowner = true
//
// if exist owner in LS ftp and 'owner' => False isowner = result of owner(file) == user(logged with ftp)
$owner_computed = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true; $owner_computed = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true;
@ -1083,7 +1044,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1120,7 +1080,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
*/ */
protected function _fopen($path, $mode = 'rb') protected function _fopen($path, $mode = 'rb')
{ {
// try ftp stream wrapper
if ($this->options['mode'] === 'passive' && ini_get('allow_url_fopen')) { if ($this->options['mode'] === 'passive' && ini_get('allow_url_fopen')) {
$url = ($this->isFTPS ? 'ftps' : 'ftp') . '://' . $this->options['user'] . ':' . $this->options['pass'] . '@' . $this->options['host'] . ':' . $this->options['port'] . $path; $url = ($this->isFTPS ? 'ftps' : 'ftp') . '://' . $this->options['user'] . ':' . $this->options['pass'] . '@' . $this->options['host'] . ':' . $this->options['port'] . $path;
if (strtolower($mode[0]) === 'w') { if (strtolower($mode[0]) === 'w') {
@ -1171,7 +1130,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed * Create dir and return created dir path or false on failed
@ -1423,7 +1381,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$localPath = $dir . DIRECTORY_SEPARATOR . $basename; $localPath = $dir . DIRECTORY_SEPARATOR . $basename;
if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) { if (!ftp_get($this->connect, $localPath, $path, FTP_BINARY)) {
//cleanup
$this->rmdirRecursive($dir); $this->rmdirRecursive($dir);
return false; return false;
} }
@ -1432,7 +1389,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$this->archiveSize = 0; $this->archiveSize = 0;
// find symlinks and check extracted items
$checkRes = $this->checkExtractItems($dir); $checkRes = $this->checkExtractItems($dir);
if ($checkRes['symlinks']) { if ($checkRes['symlinks']) {
$this->rmdirRecursive($dir); $this->rmdirRecursive($dir);
@ -1447,13 +1403,11 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$filesToProcess = self::listFilesInDirectory($dir, true); $filesToProcess = self::listFilesInDirectory($dir, true);
// no files - extract error ?
if (empty($filesToProcess)) { if (empty($filesToProcess)) {
$this->rmdirRecursive($dir); $this->rmdirRecursive($dir);
return false; return false;
} }
// check max files size
if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
$this->rmdirRecursive($dir); $this->rmdirRecursive($dir);
return $this->setError(elFinder::ERROR_ARC_MAXSIZE); return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
@ -1461,14 +1415,11 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
$extractTo = $this->extractToNewdir; // 'auto', ture or false $extractTo = $this->extractToNewdir; // 'auto', ture or false
// archive contains one item - extract in archive dir
$name = ''; $name = '';
$src = $dir . DIRECTORY_SEPARATOR . $filesToProcess[0]; $src = $dir . DIRECTORY_SEPARATOR . $filesToProcess[0];
if (($extractTo === 'auto' || !$extractTo) && count($filesToProcess) === 1 && is_file($src)) { if (($extractTo === 'auto' || !$extractTo) && count($filesToProcess) === 1 && is_file($src)) {
$name = $filesToProcess[0]; $name = $filesToProcess[0];
} else if ($extractTo === 'auto' || $extractTo) { } else if ($extractTo === 'auto' || $extractTo) {
// for several files - create new directory
// create unique name for directory
$src = $dir; $src = $dir;
$splits = elFinder::splitFileExtention(basename($path)); $splits = elFinder::splitFileExtention(basename($path));
$name = $splits[0]; $name = $splits[0];
@ -1570,7 +1521,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
*/ */
protected function _archive($dir, $files, $name, $arc) protected function _archive($dir, $files, $name, $arc)
{ {
// get current directory
$cwd = getcwd(); $cwd = getcwd();
$tmpDir = $this->tempDir(); $tmpDir = $this->tempDir();
@ -1578,9 +1528,7 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
return false; return false;
} }
//download data
if (!$this->ftp_download_files($dir, $files, $tmpDir)) { if (!$this->ftp_download_files($dir, $files, $tmpDir)) {
//cleanup
$this->rmdirRecursive($tmpDir); $this->rmdirRecursive($tmpDir);
return false; return false;
} }
@ -1593,7 +1541,6 @@ class elFinderVolumeFTP extends elFinderVolumeDriver
} }
} }
//cleanup
if (!$this->rmdirRecursive($tmpDir)) { if (!$this->rmdirRecursive($tmpDir)) {
return false; return false;
} }

View File

@ -138,9 +138,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$this->options['mimeDetect'] = 'internal'; $this->options['mimeDetect'] = 'internal';
} }
/*********************************************************************/
/* ORIGINAL FUNCTIONS */
/*********************************************************************/
/** /**
* Get Parent ID, Item ID, Parent Path as an array from path. * Get Parent ID, Item ID, Parent Path as an array from path.
@ -330,7 +327,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$root = ''; $root = '';
if ($this->root === '/') { if ($this->root === '/') {
// get root id
if ($res = $this->_gd_getFile('/', 'id')) { if ($res = $this->_gd_getFile('/', 'id')) {
$root = $res->getId(); $root = $res->getId();
} }
@ -593,7 +589,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$byteCount = 0; $byteCount = 0;
$giantChunk = ''; $giantChunk = '';
while (!feof($handle)) { while (!feof($handle)) {
// fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
$chunk = fread($handle, 8192); $chunk = fread($handle, 8192);
$byteCount += strlen($chunk); $byteCount += strlen($chunk);
$giantChunk .= $chunk; $giantChunk .= $chunk;
@ -605,9 +600,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return $giantChunk; return $giantChunk;
} }
/*********************************************************************/
/* EXTENDED FUNCTIONS */
/*********************************************************************/
/** /**
* Prepare * Prepare
@ -865,9 +857,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]); return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]);
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare FTP connection * Prepare FTP connection
@ -909,7 +898,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
if (!$serviceAccountConfig) { if (!$serviceAccountConfig) {
$aTokenFile = ''; $aTokenFile = '';
if ($this->options['refresh_token']) { if ($this->options['refresh_token']) {
// permanent mount
$aToken = $this->options['refresh_token']; $aToken = $this->options['refresh_token'];
$this->options['access_token'] = ''; $this->options['access_token'] = '';
$tmp = elFinder::getStaticVar('commonTempPath'); $tmp = elFinder::getStaticVar('commonTempPath');
@ -923,7 +911,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
} }
} }
} else { } else {
// make net mount key for network mount
if (is_array($this->options['access_token'])) { if (is_array($this->options['access_token'])) {
$aToken = !empty($this->options['access_token']['refresh_token']) $aToken = !empty($this->options['access_token']['refresh_token'])
? $this->options['access_token']['refresh_token'] ? $this->options['access_token']['refresh_token']
@ -997,7 +984,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return $this->setError($errors); return $this->setError($errors);
} }
// normalize root path
if ($this->options['path'] == 'root') { if ($this->options['path'] == 'root') {
$this->options['path'] = '/'; $this->options['path'] = '/';
} }
@ -1028,10 +1014,8 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$this->tmp = $tmp; $this->tmp = $tmp;
} }
// This driver dose not support `syncChkAsTs`
$this->options['syncChkAsTs'] = false; $this->options['syncChkAsTs'] = false;
// 'lsPlSleep' minmum 10 sec
$this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']); $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
if ($this->options['useGoogleTmb']) { if ($this->options['useGoogleTmb']) {
@ -1039,7 +1023,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$this->options['tmbPath'] = ''; $this->options['tmbPath'] = '';
} }
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
return true; return true;
@ -1054,7 +1037,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
{ {
parent::configure(); parent::configure();
// fallback of $this->tmp
if (!$this->tmp && $this->tmbPathWritable) { if (!$this->tmp && $this->tmbPathWritable) {
$this->tmp = $this->tmbPath; $this->tmp = $this->tmbPath;
} }
@ -1064,9 +1046,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
} }
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection. * Close opened connection.
@ -1136,7 +1115,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
protected function doSearch($path, $q, $mimes) protected function doSearch($path, $q, $mimes)
{ {
if (!empty($this->doSearchCurrentQuery['matchMethod'])) { if (!empty($this->doSearchCurrentQuery['matchMethod'])) {
// has custom match method use elFinderVolumeDriver::doSearch()
return parent::doSearch($path, $q, $mimes); return parent::doSearch($path, $q, $mimes);
} }
@ -1310,7 +1288,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$name = $this->tmbname($stat); $name = $this->tmbname($stat);
$tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
// copy image into tmbPath so some drivers does not store files on local fs
if (!$data = $this->_gd_getThumbnail($path)) { if (!$data = $this->_gd_getThumbnail($path)) {
return false; return false;
} }
@ -1326,13 +1303,11 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return false; return false;
} }
/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
} else { } else {
if ($this->options['tmbCrop']) { if ($this->options['tmbCrop']) {
/* Resize and crop if image bigger than thumbnail */
if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
} }
@ -1387,7 +1362,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return parent::getContentUrl($hash, $options); return parent::getContentUrl($hash, $options);
} }
if (!empty($options['temporary'])) { if (!empty($options['temporary'])) {
// try make temporary file
$url = parent::getContentUrl($hash, $options); $url = parent::getContentUrl($hash, $options);
if ($url) { if ($url) {
return $url; return $url;
@ -1421,7 +1395,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path. * Return parent directory path.
@ -1548,7 +1521,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return $path == $parent || strpos($path, $parent . '/') === 0; return $path == $parent || strpos($path, $parent . '/') === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -1646,7 +1618,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1697,7 +1668,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
'headers' => array('Authorization: Bearer ' . $access_token), 'headers' => array('Authorization: Bearer ' . $access_token),
); );
// to support range request
if (func_num_args() > 2) { if (func_num_args() > 2) {
$opts = func_get_arg(2); $opts = func_get_arg(2);
} else { } else {
@ -1732,7 +1702,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed. * Create dir and return created dir path or false on failed.
@ -1755,7 +1724,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$file->setMimeType(self::DIRMIME); $file->setMimeType(self::DIRMIME);
$file->setParents([$parentId]); $file->setParents([$parentId]);
//create the Folder in the Parent
$obj = $this->service->files->create($file); $obj = $this->service->files->create($file);
if ($obj instanceof Google_Service_Drive_DriveFile) { if ($obj instanceof Google_Service_Drive_DriveFile) {
@ -1818,7 +1786,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
$file = new \Google_Service_Drive_DriveFile(); $file = new \Google_Service_Drive_DriveFile();
$file->setName($name); $file->setName($name);
//Set the Parent id
list(, $parentId) = $this->_gd_splitPath($targetDir); list(, $parentId) = $this->_gd_splitPath($targetDir);
$file->setParents([$parentId]); $file->setParents([$parentId]);
@ -1850,11 +1817,9 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
list($removeParents, $itemId) = $this->_gd_splitPath($source); list($removeParents, $itemId) = $this->_gd_splitPath($source);
$target = $this->_normpath($targetDir . '/' . $itemId); $target = $this->_normpath($targetDir . '/' . $itemId);
try { try {
//moving and renaming a file or directory
$files = new \Google_Service_Drive_DriveFile(); $files = new \Google_Service_Drive_DriveFile();
$files->setName($name); $files->setName($name);
//Set new Parent and remove old parent
list(, $addParents) = $this->_gd_splitPath($targetDir); list(, $addParents) = $this->_gd_splitPath($targetDir);
$opts = ['addParents' => $addParents, 'removeParents' => $removeParents]; $opts = ['addParents' => $addParents, 'removeParents' => $removeParents];
@ -1976,7 +1941,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
} }
} }
// set chunk size (max: 100MB)
$chunkSizeBytes = 100 * 1024 * 1024; $chunkSizeBytes = 100 * 1024 * 1024;
if ($size > 0) { if ($size > 0) {
$memory = elFinder::getIniBytes('memory_limit'); $memory = elFinder::getIniBytes('memory_limit');
@ -1987,7 +1951,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
if ($size > $chunkSizeBytes) { if ($size > $chunkSizeBytes) {
$client = $this->client; $client = $this->client;
// Call the API with the media upload, defer so it doesn't immediately return.
$client->setDefer(true); $client->setDefer(true);
if ($mode === 'insert') { if ($mode === 'insert') {
$request = $this->service->files->create($file, [ $request = $this->service->files->create($file, [
@ -1999,22 +1962,14 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
]); ]);
} }
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes); $media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes);
$media->setFileSize($size); $media->setFileSize($size);
// Upload the various chunks. $status will be false until the process is
// complete.
$status = false; $status = false;
while (!$status && !feof($fp)) { while (!$status && !feof($fp)) {
elFinder::checkAborted(); elFinder::checkAborted();
// read until you get $chunkSizeBytes from TESTFILE
// fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
// An example of a read buffered file is when reading from a URL
$chunk = $this->_gd_readFileChunk($fp, $chunkSizeBytes); $chunk = $this->_gd_readFileChunk($fp, $chunkSizeBytes);
$status = $media->nextChunk($chunk); $status = $media->nextChunk($chunk);
} }
// The final value of $status will be the data from the API for the object
// that has been uploaded.
if ($status !== false) { if ($status !== false) {
$obj = $status; $obj = $status;
} }
@ -2099,7 +2054,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
**/ **/
protected function _checkArchivers() protected function _checkArchivers()
{ {
// die('Not yet implemented. (_checkArchivers)');
return []; return [];
} }
@ -2126,7 +2080,6 @@ class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
protected function _unpack($path, $arc) protected function _unpack($path, $arc)
{ {
die('Not yet implemented. (_unpack)'); die('Not yet implemented. (_unpack)');
//return false;
} }
/** /**

View File

@ -47,11 +47,7 @@ class elFinderVolumeGroup extends elFinderVolumeDriver
); );
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/*********************** paths/urls *************************/
/** /**
* @inheritdoc * @inheritdoc
@ -119,7 +115,6 @@ class elFinderVolumeGroup extends elFinderVolumeDriver
/***************** file stat ********************/
/** /**
* {@inheritDoc} * {@inheritDoc}
@ -156,7 +151,6 @@ class elFinderVolumeGroup extends elFinderVolumeDriver
{ {
return false; return false;
} }
/******************** file/dir content *********************/
/** /**
* {@inheritDoc} * {@inheritDoc}
@ -190,7 +184,6 @@ class elFinderVolumeGroup extends elFinderVolumeDriver
return true; return true;
} }
/******************** file/dir manipulations *************************/
/** /**
* {@inheritDoc} * {@inheritDoc}

View File

@ -1,6 +1,5 @@
<?php <?php
// Implement similar functionality in PHP 5.2 or 5.3
// http://php.net/manual/class.recursivecallbackfilteriterator.php#110974 // http://php.net/manual/class.recursivecallbackfilteriterator.php#110974
if (!class_exists('RecursiveCallbackFilterIterator', false)) { if (!class_exists('RecursiveCallbackFilterIterator', false)) {
class RecursiveCallbackFilterIterator extends RecursiveFilterIterator class RecursiveCallbackFilterIterator extends RecursiveFilterIterator
@ -84,9 +83,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$this->options['statCorrector'] = null; // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}` $this->options['statCorrector'] = null; // callable to correct stat data `function(&$stat, $path, $statOwner, $volumeDriveInstance){}`
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare driver before mount volume. * Prepare driver before mount volume.
@ -96,14 +92,12 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
**/ **/
protected function init() protected function init()
{ {
// Normalize directory separator for windows
if (DIRECTORY_SEPARATOR !== '/') { if (DIRECTORY_SEPARATOR !== '/') {
foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) { foreach (array('path', 'tmbPath', 'tmpPath', 'quarantine') as $key) {
if (!empty($this->options[$key])) { if (!empty($this->options[$key])) {
$this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]); $this->options[$key] = str_replace('/', DIRECTORY_SEPARATOR, $this->options[$key]);
} }
} }
// PHP >= 7.1 Supports UTF-8 path on Windows
if (version_compare(PHP_VERSION, '7.1', '>=')) { if (version_compare(PHP_VERSION, '7.1', '>=')) {
$this->options['encoding'] = ''; $this->options['encoding'] = '';
$this->options['locale'] = ''; $this->options['locale'] = '';
@ -112,7 +106,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
if (!$cwd = getcwd()) { if (!$cwd = getcwd()) {
return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().'); return $this->setError('elFinder LocalVolumeDriver requires a result of getcwd().');
} }
// detect systemRoot
if (!isset($this->options['systemRoot'])) { if (!isset($this->options['systemRoot'])) {
if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) { if ($cwd[0] === DIRECTORY_SEPARATOR || $this->root[0] === DIRECTORY_SEPARATOR) {
$this->systemRoot = DIRECTORY_SEPARATOR; $this->systemRoot = DIRECTORY_SEPARATOR;
@ -133,7 +126,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
if (is_null($this->options['syncCheckFunc'])) { if (is_null($this->options['syncCheckFunc'])) {
$this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify'); $this->options['syncCheckFunc'] = array($this, 'localFileSystemInotify');
} }
// check 'statCorrector'
if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) { if (empty($this->options['statCorrector']) || !is_callable($this->options['statCorrector'])) {
$this->options['statCorrector'] = null; $this->options['statCorrector'] = null;
} }
@ -153,7 +145,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$hiddens = array(); $hiddens = array();
$root = $this->stat($this->root); $root = $this->stat($this->root);
// check thumbnails path
if (!empty($this->options['tmbPath'])) { if (!empty($this->options['tmbPath'])) {
if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) { if (strpos($this->options['tmbPath'], DIRECTORY_SEPARATOR) === false) {
$hiddens['tmb'] = $this->options['tmbPath']; $hiddens['tmb'] = $this->options['tmbPath'];
@ -162,7 +153,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']); $this->options['tmbPath'] = $this->_normpath($this->options['tmbPath']);
} }
} }
// check temp path
if (!empty($this->options['tmpPath'])) { if (!empty($this->options['tmpPath'])) {
if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) { if (strpos($this->options['tmpPath'], DIRECTORY_SEPARATOR) === false) {
$hiddens['temp'] = $this->options['tmpPath']; $hiddens['temp'] = $this->options['tmpPath'];
@ -171,7 +161,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']); $this->options['tmpPath'] = $this->_normpath($this->options['tmpPath']);
} }
} }
// check quarantine path
if (!empty($this->options['quarantine'])) { if (!empty($this->options['quarantine'])) {
if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) { if (strpos($this->options['quarantine'], DIRECTORY_SEPARATOR) === false) {
$hiddens['quarantine'] = $this->options['quarantine']; $hiddens['quarantine'] = $this->options['quarantine'];
@ -183,12 +172,10 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
parent::configure(); parent::configure();
// check tmbPath
if (!$this->tmbPath && isset($hiddens['tmb'])) { if (!$this->tmbPath && isset($hiddens['tmb'])) {
unset($hiddens['tmb']); unset($hiddens['tmb']);
} }
// if no thumbnails url - try detect it
if ($root['read'] && !$this->tmbURL && $this->URL) { if ($root['read'] && !$this->tmbURL && $this->URL) {
if (strpos($this->tmbPath, $this->root) === 0) { if (strpos($this->tmbPath, $this->root) === 0) {
$this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1)); $this->tmbURL = $this->URL . str_replace(DIRECTORY_SEPARATOR, '/', substr($this->tmbPath, strlen($this->root) + 1));
@ -198,7 +185,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
} }
} }
// set $this->tmp by options['tmpPath']
$this->tmp = ''; $this->tmp = '';
if (!empty($this->options['tmpPath'])) { if (!empty($this->options['tmpPath'])) {
if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) { if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'], $this->options['dirMode'], true)) && is_writable($this->options['tmpPath'])) {
@ -213,7 +199,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$this->tmp = $tmp; $this->tmp = $tmp;
} }
// check quarantine dir
$this->quarantine = ''; $this->quarantine = '';
if (!empty($this->options['quarantine'])) { if (!empty($this->options['quarantine'])) {
if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) { if ((is_dir($this->options['quarantine']) || mkdir($this->options['quarantine'], $this->options['dirMode'], true)) && is_writable($this->options['quarantine'])) {
@ -283,31 +268,22 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self'; $cmd = $inotifywait . ' ' . escapeshellarg($path) . ' -t ' . $standby . ' -e moved_to,moved_from,move,close_write,delete,delete_self';
$this->procExec($cmd, $o, $r); $this->procExec($cmd, $o, $r);
if ($r === 0) { if ($r === 0) {
// changed
clearstatcache(); clearstatcache();
if (file_exists($path)) { if (file_exists($path)) {
$mtime = filemtime($path); // error on busy? $mtime = filemtime($path); // error on busy?
return $mtime ? $mtime : time(); return $mtime ? $mtime : time();
} else { } else {
// target was removed
return 0; return 0;
} }
} else if ($r === 2) { } else if ($r === 2) {
// not changed (timeout)
return $compare; return $compare;
} }
// error
// cache to $_SESSION
$this->sessionCache['localFileSystemInotify_disable'] = true; $this->sessionCache['localFileSystemInotify_disable'] = true;
$this->session->set($this->id, $this->sessionCache); $this->session->set($this->id, $this->sessionCache);
return false; return false;
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/*********************** paths/urls *************************/
/** /**
* Return parent directory path * Return parent directory path
@ -431,7 +407,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
if (strpos($path, $this->root) === 0) { if (strpos($path, $this->root) === 0) {
return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR); return ltrim(substr($path, strlen($this->root)), DIRECTORY_SEPARATOR);
} else { } else {
// for link
return $path; return $path;
} }
} }
@ -495,7 +470,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
@ -524,7 +498,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
return $stat; return $stat;
} }
//Verifies the given path is the root or is inside the root. Prevents directory traveral.
if (!$this->_inpath($path, $this->root)) { if (!$this->_inpath($path, $this->root)) {
return $stat; return $stat;
} }
@ -576,7 +549,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
if (!isset($stat['mime'])) { if (!isset($stat['mime'])) {
$stat['mime'] = $dir ? 'directory' : $this->mimetype($path); $stat['mime'] = $dir ? 'directory' : $this->mimetype($path);
} }
//logical rights first
$stat['read'] = ($linkreadable || $readable) ? null : false; $stat['read'] = ($linkreadable || $readable) ? null : false;
$stat['write'] = is_writable($path) ? null : false; $stat['write'] = is_writable($path) ? null : false;
@ -709,7 +681,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
? $s[0] . 'x' . $s[1] ? $s[0] . 'x' . $s[1]
: false; : false;
} }
/******************** file/dir content *********************/
/** /**
* Return symlink target file * Return symlink target file
@ -817,7 +788,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$stat = array_merge($stat, $this->getOwnerStat($uid, $gid)); $stat = array_merge($stat, $this->getOwnerStat($uid, $gid));
} }
//logical rights first
$stat['read'] = ($linkreadable || $file->isReadable()) ? null : false; $stat['read'] = ($linkreadable || $file->isReadable()) ? null : false;
$stat['write'] = $file->isWritable() ? null : false; $stat['write'] = $file->isWritable() ? null : false;
$stat['locked'] = $dirWritable ? null : true; $stat['locked'] = $dirWritable ? null : true;
@ -876,7 +846,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
return (is_resource($fp) && fclose($fp)); return (is_resource($fp) && fclose($fp));
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed * Create dir and return created dir path or false on failed
@ -1029,7 +998,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) { if (($isCmdCopy || !rename($uri, $path)) && !copy($uri, $path)) {
return false; return false;
} }
// keep timestamp on upload
if ($mtime && $this->ARGS['cmd'] === 'upload') { if ($mtime && $this->ARGS['cmd'] === 'upload') {
touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time()); touch($path, isset($this->options['keepTimestamp']['upload']) ? $mtime : time());
} }
@ -1133,38 +1101,32 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
return false; return false;
} }
// insurance unexpected shutdown
register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir)); register_shutdown_function(array($this, 'rmdirRecursive'), realpath($dir));
chmod($dir, 0777); chmod($dir, 0777);
// copy in quarantine
if (!is_readable($path) || ($archive && !copy($path, $archive))) { if (!is_readable($path) || ($archive && !copy($path, $archive))) {
return false; return false;
} }
// extract in quarantine
try { try {
$this->unpackArchive($path, $arc, $archive ? true : $dir); $this->unpackArchive($path, $arc, $archive ? true : $dir);
} catch(Exception $e) { } catch(Exception $e) {
return $this->setError($e->getMessage()); return $this->setError($e->getMessage());
} }
// get files list
try { try {
$ls = self::localScandir($dir); $ls = self::localScandir($dir);
} catch (Exception $e) { } catch (Exception $e) {
return false; return false;
} }
// no files - extract error ?
if (empty($ls)) { if (empty($ls)) {
return false; return false;
} }
$this->archiveSize = 0; $this->archiveSize = 0;
// find symlinks and check extracted items
$checkRes = $this->checkExtractItems($dir); $checkRes = $this->checkExtractItems($dir);
if ($checkRes['symlinks']) { if ($checkRes['symlinks']) {
self::localRmdirRecursive($dir); self::localRmdirRecursive($dir);
@ -1177,7 +1139,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
} }
} }
// check max files size
if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) { if ($this->options['maxArcFilesSize'] > 0 && $this->options['maxArcFilesSize'] < $this->archiveSize) {
$this->delTree($dir); $this->delTree($dir);
return $this->setError(elFinder::ERROR_ARC_MAXSIZE); return $this->setError(elFinder::ERROR_ARC_MAXSIZE);
@ -1185,14 +1146,11 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
$extractTo = $this->extractToNewdir; // 'auto', ture or false $extractTo = $this->extractToNewdir; // 'auto', ture or false
// archive contains one item - extract in archive dir
$name = ''; $name = '';
$src = $dir . DIRECTORY_SEPARATOR . $ls[0]; $src = $dir . DIRECTORY_SEPARATOR . $ls[0];
if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) { if (($extractTo === 'auto' || !$extractTo) && count($ls) === 1 && is_file($src)) {
$name = $ls[0]; $name = $ls[0];
} else if ($extractTo === 'auto' || $extractTo) { } else if ($extractTo === 'auto' || $extractTo) {
// for several files - create new directory
// create unique name for directory
$src = $dir; $src = $dir;
$splits = elFinder::splitFileExtention(basename($path)); $splits = elFinder::splitFileExtention(basename($path));
$name = $splits[0]; $name = $splits[0];
@ -1228,7 +1186,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
return (is_array($result) || file_exists($result)) ? $result : false; return (is_array($result) || file_exists($result)) ? $result : false;
} }
//TODO: Add return statement here
return false; return false;
} }
@ -1250,7 +1207,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
return $this->makeArchive($dir, $files, $name, $arc); return $this->makeArchive($dir, $files, $name, $arc);
} }
/******************** Over write functions *************************/
/** /**
* File path of local server side work file path * File path of local server side work file path
@ -1291,12 +1247,10 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
protected function isNameExists($path) protected function isNameExists($path)
{ {
$exists = file_exists($this->convEncIn($path)); $exists = file_exists($this->convEncIn($path));
// restore locale
$this->convEncOut(); $this->convEncOut();
return $exists ? $this->stat($path) : false; return $exists ? $this->stat($path) : false;
} }
/******************** Over write (Optimized) functions *************************/
/** /**
* Recursive files search * Recursive files search
@ -1313,7 +1267,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
protected function doSearch($path, $q, $mimes) protected function doSearch($path, $q, $mimes)
{ {
if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) { if (!empty($this->doSearchCurrentQuery['matchMethod']) || $this->encoding || !class_exists('FilesystemIterator', false)) {
// has custom match method or non UTF-8, use elFinderVolumeDriver::doSearch()
return parent::doSearch($path, $q, $mimes); return parent::doSearch($path, $q, $mimes);
} }
@ -1400,8 +1353,6 @@ class elFinderVolumeLocalFileSystem extends elFinderVolumeDriver
public function localFileSystemSearchIteratorFilter($file, $key, $iterator) public function localFileSystemSearchIteratorFilter($file, $key, $iterator)
{ {
/* @var FilesystemIterator $file */
/* @var RecursiveDirectoryIterator $iterator */
$name = $file->getFilename(); $name = $file->getFilename();
if ($this->doSearchCurrentQuery['excludes']) { if ($this->doSearchCurrentQuery['excludes']) {
foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) { foreach ($this->doSearchCurrentQuery['excludes'] as $exclude) {

View File

@ -86,9 +86,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
$this->options['mimeDetect'] = 'internal'; $this->options['mimeDetect'] = 'internal';
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare driver before mount volume. * Prepare driver before mount volume.
@ -144,10 +141,8 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
$this->updateCache($this->options['path'], $this->_stat($this->options['path'])); $this->updateCache($this->options['path'], $this->_stat($this->options['path']));
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
// check isLocalhost
$this->isLocalhost = $this->options['isLocalhost'] || $this->options['host'] === 'localhost' || $this->options['host'] === '127.0.0.1' || $this->options['host'] === '::1'; $this->isLocalhost = $this->options['isLocalhost'] || $this->options['host'] === 'localhost' || $this->options['host'] === '127.0.0.1' || $this->options['host'] === '::1';
return true; return true;
@ -178,7 +173,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
$this->tmpPath = $tmp; $this->tmpPath = $tmp;
} }
// fallback of $this->tmp
if (!$this->tmpPath && $this->tmbPathWritable) { if (!$this->tmpPath && $this->tmbPathWritable) {
$this->tmpPath = $this->tmbPath; $this->tmpPath = $this->tmbPath;
} }
@ -246,13 +240,9 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
{ {
$sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\', \'%d\', \'%d\', 0, 0)'; $sql = 'INSERT INTO %s (`parent_id`, `name`, `size`, `mtime`, `mime`, `content`, `read`, `write`, `locked`, `hidden`, `width`, `height`) VALUES (\'%s\', \'%s\', 0, %d, \'%s\', \'\', \'%d\', \'%d\', \'%d\', \'%d\', 0, 0)';
$sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write'], $this->defaults['locked'], $this->defaults['hidden']); $sql = sprintf($sql, $this->tbf, $path, $this->db->real_escape_string($name), time(), $mime, $this->defaults['read'], $this->defaults['write'], $this->defaults['locked'], $this->defaults['hidden']);
// echo $sql;
return $this->query($sql) && $this->db->affected_rows > 0; return $this->query($sql) && $this->db->affected_rows > 0;
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Cache dir contents * Cache dir contents
@ -357,7 +347,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
protected function doSearch($path, $q, $mimes) protected function doSearch($path, $q, $mimes)
{ {
if (!empty($this->doSearchCurrentQuery['matchMethod'])) { if (!empty($this->doSearchCurrentQuery['matchMethod'])) {
// has custom match method use elFinderVolumeDriver::doSearch()
return parent::doSearch($path, $q, $mimes); return parent::doSearch($path, $q, $mimes);
} }
@ -444,7 +433,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path * Return parent directory path
@ -570,7 +558,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
: in_array($parent, $this->getParents($path)); : in_array($parent, $this->getParents($path));
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -655,7 +642,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'] . 'x' . $stat['height'] : ''; return ($stat = $this->stat($path)) && isset($stat['width']) && isset($stat['height']) ? $stat['width'] . 'x' . $stat['height'] : '';
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -720,7 +706,6 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed * Create dir and return created dir path or false on failed

View File

@ -118,9 +118,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$this->options['mimeDetect'] = 'internal'; $this->options['mimeDetect'] = 'internal';
} }
/*********************************************************************/
/* ORIGINAL FUNCTIONS */
/*********************************************************************/
/** /**
* Obtains a new access token from OAuth. This token is valid for one hour. * Obtains a new access token from OAuth. This token is valid for one hour.
@ -164,7 +161,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
); );
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $fields, CURLOPT_POSTFIELDS => $fields,
@ -234,7 +230,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$curl = curl_init(); $curl = curl_init();
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, // i am sending post data CURLOPT_POST => true, // i am sending post data
CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id']) CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id'])
@ -287,8 +282,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
file_put_contents($this->aTokenFile, json_encode($token)); file_put_contents($this->aTokenFile, json_encode($token));
$this->options['accessToken'] = $json; $this->options['accessToken'] = $json;
} else if (!empty($this->options['netkey'])) { } else if (!empty($this->options['netkey'])) {
// OAuth2 refresh token can be used only once,
// so update it if it is the same as the token file
$aTokenFile = $this->_od_getATokenFile(); $aTokenFile = $this->_od_getATokenFile();
if ($aTokenFile && is_file($aTokenFile)) { if ($aTokenFile && is_file($aTokenFile)) {
if ($_token = json_decode(file_get_contents($aTokenFile))) { if ($_token = json_decode(file_get_contents($aTokenFile))) {
@ -298,7 +291,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
} }
} }
$this->options['accessToken'] = $json; $this->options['accessToken'] = $json;
// update session value
elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $this->options['accessToken']); elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $this->options['accessToken']);
$this->session->set('OneDriveTokens', $token); $this->session->set('OneDriveTokens', $token);
} else { } else {
@ -349,7 +341,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$curl = curl_init($url); $curl = curl_init($url);
$defaultOptions = array( $defaultOptions = array(
// General options.
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array( CURLOPT_HTTPHEADER => array(
'Content-Type: application/json', 'Content-Type: application/json',
@ -556,7 +547,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
throw new Exception('Data can not be acquired from the source.'); throw new Exception('Data can not be acquired from the source.');
} }
// create upload session
if ($itemId) { if ($itemId) {
$url = self::API_URL . $itemId . '/createUploadSession'; $url = self::API_URL . $itemId . '/createUploadSession';
} else { } else {
@ -713,9 +703,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken); return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken);
} }
/*********************************************************************/
/* OVERRIDE FUNCTIONS */
/*********************************************************************/
/** /**
* Prepare * Prepare
@ -763,7 +750,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
if ($code) { if ($code) {
try { try {
if (!empty($options['id'])) { if (!empty($options['id'])) {
// Obtain the token using the code received by the OneDrive API
$this->session->set('OneDriveTokens', $this->session->set('OneDriveTokens',
$this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $code, $options['id'])); $this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $code, $options['id']));
@ -843,7 +829,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$this->session->set('OneDriveTokens', (object)array('token' => null)); $this->session->set('OneDriveTokens', (object)array('token' => null));
$offline = ''; $offline = '';
// Gets a log in URL with sufficient privileges from the OneDrive API
if (!empty($options['offline'])) { if (!empty($options['offline'])) {
$offline = ' offline_access'; $offline = ' offline_access';
} }
@ -949,9 +934,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************************************************************/
/* INIT AND CONFIGURE */
/*********************************************************************/
/** /**
* Prepare FTP connection * Prepare FTP connection
@ -981,7 +963,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
throw new Exception('Required option `accessToken` is invalid JSON.'); throw new Exception('Required option `accessToken` is invalid JSON.');
} }
// make net mount key
if (empty($this->options['netkey'])) { if (empty($this->options['netkey'])) {
$this->netMountKey = $this->_od_getInitialToken(); $this->netMountKey = $this->_od_getInitialToken();
} else { } else {
@ -1002,7 +983,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
} }
} }
} else if (is_file($this->aTokenFile)) { } else if (is_file($this->aTokenFile)) {
// If the refresh token is the same as the permanent volume
$this->token = json_decode(file_get_contents($this->aTokenFile)); $this->token = json_decode(file_get_contents($this->aTokenFile));
} }
} }
@ -1024,13 +1004,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
if ($error) { if ($error) {
if (empty($this->options['netkey']) && $this->tmbPrefix) { if (empty($this->options['netkey']) && $this->tmbPrefix) {
// for delete thumbnail
$this->netunmount(null, null); $this->netunmount(null, null);
} }
return false; return false;
} }
// normalize root path
if ($this->options['path'] == 'root') { if ($this->options['path'] == 'root') {
$this->options['path'] = '/'; $this->options['path'] = '/';
} }
@ -1053,10 +1031,8 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$this->rootName = $this->options['alias']; $this->rootName = $this->options['alias'];
// This driver dose not support `syncChkAsTs`
$this->options['syncChkAsTs'] = false; $this->options['syncChkAsTs'] = false;
// 'lsPlSleep' minmum 10 sec
$this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']); $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
$this->queryOptions = array( $this->queryOptions = array(
@ -1071,7 +1047,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$this->queryOptions['query']['expand'] = 'thumbnails(select=small)'; $this->queryOptions['query']['expand'] = 'thumbnails(select=small)';
} }
// enable command archive
$this->options['useRemoteArchive'] = true; $this->options['useRemoteArchive'] = true;
return true; return true;
@ -1086,15 +1061,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
{ {
parent::configure(); parent::configure();
// fallback of $this->tmp
if (!$this->tmp && $this->tmbPathWritable) { if (!$this->tmp && $this->tmbPathWritable) {
$this->tmp = $this->tmbPath; $this->tmp = $this->tmbPath;
} }
} }
/*********************************************************************/
/* FS API */
/*********************************************************************/
/** /**
* Close opened connection. * Close opened connection.
@ -1268,7 +1239,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$name = $this->tmbname($stat); $name = $this->tmbname($stat);
$tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name; $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
// copy image into tmbPath so some drivers does not store files on local fs
if (!$data = $this->_od_getThumbnail($path)) { if (!$data = $this->_od_getThumbnail($path)) {
return false; return false;
} }
@ -1284,13 +1254,11 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return false; return false;
} }
/* If image smaller or equal thumbnail size - just fitting to thumbnail square */
if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) { if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
$result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png'); $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
} else { } else {
if ($this->options['tmbCrop']) { if ($this->options['tmbCrop']) {
/* Resize and crop if image bigger than thumbnail */
if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) { if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
$result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png'); $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
} }
@ -1344,7 +1312,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return parent::getContentUrl($hash, $options); return parent::getContentUrl($hash, $options);
} }
if (!empty($options['temporary'])) { if (!empty($options['temporary'])) {
// try make temporary file
$url = parent::getContentUrl($hash, $options); $url = parent::getContentUrl($hash, $options);
if ($url) { if ($url) {
return $url; return $url;
@ -1384,7 +1351,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return $res; return $res;
} }
/*********************** paths/urls *************************/
/** /**
* Return parent directory path. * Return parent directory path.
@ -1505,7 +1471,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return $path == $parent || strpos($path, $parent . '/') === 0; return $path == $parent || strpos($path, $parent . '/') === 0;
} }
/***************** file stat ********************/
/** /**
* Return stat for given path. * Return stat for given path.
* Stat contains following fields: * Stat contains following fields:
@ -1577,7 +1542,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return ''; return '';
} }
//$cache = $this->_od_getFileRaw($path);
if (func_num_args() > 2) { if (func_num_args() > 2) {
$args = func_get_arg(2); $args = func_get_arg(2);
} else { } else {
@ -1632,7 +1596,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
return $ret; return $ret;
} }
/******************** file/dir content *********************/
/** /**
* Return files list in directory. * Return files list in directory.
@ -1669,7 +1632,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
'headers' => array('Authorization: Bearer ' . $this->token->data->access_token), 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token),
); );
// to support range request
if (func_num_args() > 2) { if (func_num_args() > 2) {
$opts = func_get_arg(2); $opts = func_get_arg(2);
} else { } else {
@ -1701,7 +1663,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
} }
} }
/******************** file/dir manipulations *************************/
/** /**
* Create dir and return created dir path or false on failed. * Create dir and return created dir path or false on failed.
@ -1734,7 +1695,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_POSTFIELDS => json_encode($data),
)); ));
//create the Folder in the Parent
$result = curl_exec($curl); $result = curl_exec($curl);
curl_close($curl); curl_close($curl);
$folder = json_decode($result); $folder = json_decode($result);
@ -1788,7 +1748,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
$path = $this->_joinPath($targetDir, $name); $path = $this->_joinPath($targetDir, $name);
try { try {
//Set the Parent id
list(, $parentId) = $this->_od_splitPath($targetDir); list(, $parentId) = $this->_od_splitPath($targetDir);
list(, $itemId) = $this->_od_splitPath($source); list(, $itemId) = $this->_od_splitPath($source);
@ -1926,7 +1885,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_CUSTOMREQUEST => 'DELETE',
)); ));
//unlink or delete File or Folder in the Parent
$result = curl_exec($curl); $result = curl_exec($curl);
curl_close($curl); curl_close($curl);
} catch (Exception $e) { } catch (Exception $e) {
@ -1994,7 +1952,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
} }
try { try {
// for unseekable file pointer
if (!elFinder::isSeekableStream($fp)) { if (!elFinder::isSeekableStream($fp)) {
if ($tfp = tmpfile()) { if ($tfp = tmpfile()) {
if (stream_copy_to_stream($fp, $tfp, $size? $size : -1) !== false) { if (stream_copy_to_stream($fp, $tfp, $size? $size : -1) !== false) {
@ -2004,7 +1961,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
} }
} }
//Create or Update a file
if ($itemId === '') { if ($itemId === '') {
$url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/content'; $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/content';
} else { } else {
@ -2017,14 +1973,12 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
CURLOPT_PUT => true, CURLOPT_PUT => true,
CURLOPT_INFILE => $fp, CURLOPT_INFILE => $fp,
); );
// Size
if ($size !== null) { if ($size !== null) {
$options[CURLOPT_INFILESIZE] = $size; $options[CURLOPT_INFILESIZE] = $size;
} }
curl_setopt_array($curl, $options); curl_setopt_array($curl, $options);
//create or update File in the Target
$file = json_decode(curl_exec($curl)); $file = json_decode(curl_exec($curl));
curl_close($curl); curl_close($curl);
@ -2088,7 +2042,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
**/ **/
protected function _checkArchivers() protected function _checkArchivers()
{ {
// die('Not yet implemented. (_checkArchivers)');
return array(); return array();
} }
@ -2115,7 +2068,6 @@ class elFinderVolumeOneDrive extends elFinderVolumeDriver
protected function _unpack($path, $arc) protected function _unpack($path, $arc)
{ {
die('Not yet implemented. (_unpack)'); die('Not yet implemented. (_unpack)');
//return false;
} }
/** /**

View File

@ -19,10 +19,8 @@ class elFinderVolumeTrash extends elFinderVolumeLocalFileSystem
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
// original option of the Trash
$this->options['lockEverything'] = false; // Lock all items in the trash to disable delete, move, rename. $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['alias'] = 'Trash';
$this->options['quarantine'] = ''; $this->options['quarantine'] = '';
$this->options['rootCssClass'] = 'elfinder-navbar-root-trash'; $this->options['rootCssClass'] = 'elfinder-navbar-root-trash';
@ -43,7 +41,6 @@ class elFinderVolumeTrash extends elFinderVolumeLocalFileSystem
); );
array_unshift($opts['attributes'], $attr); array_unshift($opts['attributes'], $attr);
} }
// force set `copyJoin` to true
$opts['copyJoin'] = true; $opts['copyJoin'] = true;
return parent::mount($opts); return parent::mount($opts);

View File

@ -19,10 +19,8 @@ class elFinderVolumeTrashMySQL extends elFinderVolumeMySQL
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
// original option of the Trash
$this->options['lockEverything'] = false; // Lock all items in the trash to disable delete, move, rename. $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['alias'] = 'Trash';
$this->options['quarantine'] = ''; $this->options['quarantine'] = '';
$this->options['rootCssClass'] = 'elfinder-navbar-root-trash'; $this->options['rootCssClass'] = 'elfinder-navbar-root-trash';
@ -43,7 +41,6 @@ class elFinderVolumeTrashMySQL extends elFinderVolumeMySQL
); );
array_unshift($opts['attributes'], $attr); array_unshift($opts['attributes'], $attr);
} }
// force set `copyJoin` to true
$opts['copyJoin'] = true; $opts['copyJoin'] = true;
return parent::mount($opts); return parent::mount($opts);

View File

@ -1,38 +1,8 @@
<?php <?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) //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 ver. 3.4.1 (JavaScript library), Developer Website: https://jquery.com/
//jquery ui ver. 1.12.1 (JavaScript library), Developer Website: https://jqueryui.com/ //jquery ui ver. 1.12.1 (JavaScript library), Developer Website: https://jqueryui.com/
//elFinder ver. 2.1.49, 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__ { class ControllerCommonFileManager extends ControllerCommonFileManager__ {
@ -47,28 +17,24 @@ class ControllerCommonFileManager extends ControllerCommonFileManager__ {
if($oc30) $data['token'] = $this->session->data['user_token']; // opencart 3 if($oc30) $data['token'] = $this->session->data['user_token']; // opencart 3
else $data['token'] = $this->session->data['token']; 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'])) { if (isset($this->request->get['target'])) {
$data['target'] = $this->request->get['target']; $data['target'] = $this->request->get['target'];
} else { } else {
$data['target'] = ''; $data['target'] = '';
} }
// CKEditor
if (isset($this->request->get['cke'])) { if (isset($this->request->get['cke'])) {
$data['cke'] = $this->request->get['cke']; $data['cke'] = $this->request->get['cke'];
} else { } else {
$data['cke'] = ''; $data['cke'] = '';
} }
// summernote_id
if (isset($this->request->get['summernote_id'])) { if (isset($this->request->get['summernote_id'])) {
$data['summernote_id'] = $this->request->get['summernote_id']; $data['summernote_id'] = $this->request->get['summernote_id'];
} else { } else {
$data['summernote_id'] = ''; $data['summernote_id'] = '';
} }
// Return the thumbnail for the file manager to show a thumbnail
if (isset($this->request->get['thumb'])) { if (isset($this->request->get['thumb'])) {
$data['thumb'] = $this->request->get['thumb']; $data['thumb'] = $this->request->get['thumb'];
} else { } else {
@ -131,4 +97,4 @@ class ControllerCommonFileManager extends ControllerCommonFileManager__ {
} }
?> ?>