2409 lines
112 KiB
PHP
2409 lines
112 KiB
PHP
<?php
|
|
if (!defined('DIR_APPLICATION')) { exit; }
|
|
|
|
class ControllerModuleImgOpti extends Controller {
|
|
private $error = array();
|
|
private $translations = array();
|
|
|
|
public function index() {
|
|
$data = $this->load->language('module/img_opti');
|
|
$this->document->setTitle($this->language->get('heading_title_raw'));
|
|
|
|
$this->load->model('setting/setting');
|
|
|
|
if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {
|
|
if (!isset($this->request->post['module_img_opti_cron_folders'])) {
|
|
$this->request->post['module_img_opti_cron_folders'] = array();
|
|
}
|
|
if (!isset($this->request->post['module_img_opti_cron_exclude_folders'])) {
|
|
$this->request->post['module_img_opti_cron_exclude_folders'] = array();
|
|
}
|
|
if (!isset($this->request->post['module_img_opti_cron_entities'])) {
|
|
$this->request->post['module_img_opti_cron_entities'] = array();
|
|
}
|
|
if (!isset($this->request->post['module_img_opti_wm_categories'])) {
|
|
$this->request->post['module_img_opti_wm_categories'] = array();
|
|
}
|
|
if (!isset($this->request->post['module_img_opti_wm_brands'])) {
|
|
$this->request->post['module_img_opti_wm_brands'] = array();
|
|
}
|
|
$quality_keys = array('module_img_opti_jpg_quality', 'module_img_opti_png_quality', 'module_img_opti_webp_quality', 'module_img_opti_max_width', 'module_img_opti_max_height', 'module_img_opti_engine');
|
|
$quality_changed = false;
|
|
foreach ($quality_keys as $qk) {
|
|
$old_val = $this->config->get($qk);
|
|
$new_val = isset($this->request->post[$qk]) ? $this->request->post[$qk] : null;
|
|
if ((string)$old_val !== (string)$new_val) {
|
|
$quality_changed = true;
|
|
break;
|
|
}
|
|
}
|
|
$this->model_setting_setting->editSetting('module_img_opti', $this->request->post);
|
|
$this->cache->delete('img_opti_disk_stats');
|
|
if ($quality_changed) {
|
|
$cache_file = DIR_CACHE . 'img_opti_optimized.json';
|
|
if (file_exists($cache_file)) {
|
|
@unlink($cache_file);
|
|
}
|
|
}
|
|
$this->session->data['success'] = $this->language->get('text_success');
|
|
$this->response->redirect($this->url->link('module/img_opti', 'user_token=' . $this->session->data['user_token'], true));
|
|
}
|
|
|
|
if (isset($this->session->data['success'])) {
|
|
$data['success'] = $this->session->data['success'];
|
|
unset($this->session->data['success']);
|
|
} else {
|
|
$data['success'] = '';
|
|
}
|
|
|
|
if (isset($this->error['warning'])) {
|
|
$data['error_warning'] = $this->error['warning'];
|
|
} else {
|
|
$data['error_warning'] = '';
|
|
}
|
|
|
|
$data['gd_enabled'] = extension_loaded('gd');
|
|
$data['webp_enabled'] = function_exists('imagewebp');
|
|
$data['imagick_enabled'] = extension_loaded('imagick');
|
|
|
|
$data['breadcrumbs'] = array();
|
|
$data['breadcrumbs'][] = array('text' => $this->language->get('text_home'), 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true));
|
|
$data['breadcrumbs'][] = array('text' => $this->language->get('text_extension'), 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true));
|
|
$data['breadcrumbs'][] = array('text' => $this->language->get('heading_title_raw'), 'href' => $this->url->link('module/img_opti', 'user_token=' . $this->session->data['user_token'], true));
|
|
|
|
$data['action'] = $this->url->link('module/img_opti', 'user_token=' . $this->session->data['user_token'], true);
|
|
$data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true);
|
|
|
|
$fields = array(
|
|
'module_img_opti_status' => 1,
|
|
'module_img_opti_wm_status' => 0,
|
|
'module_img_opti_ph_status' => 0,
|
|
'module_img_opti_wm_rules' => '',
|
|
'module_img_opti_ph_rules' => '',
|
|
'module_img_opti_menu_position' => 'both',
|
|
'module_img_opti_token' => '',
|
|
'module_img_opti_engine' => 'gd',
|
|
'module_img_opti_support_svg' => 0,
|
|
'module_img_opti_support_webp' => 0,
|
|
'module_img_opti_cron_new_only' => 1,
|
|
'module_img_opti_cron_folder_mode' => 'exclude',
|
|
'module_img_opti_cron_folders' => array(),
|
|
'module_img_opti_cron_exclude_folders'=> array(),
|
|
'module_img_opti_cron_entities' => array('product', 'category', 'manufacturer', 'banner', 'blog'),
|
|
'module_img_opti_cron_quality_override'=> 0,
|
|
'module_img_opti_cron_jpg_quality' => 72,
|
|
'module_img_opti_cron_webp_quality' => 80,
|
|
'module_img_opti_cron_max_width' => 1600,
|
|
'module_img_opti_cron_max_height' => 1600,
|
|
'module_img_opti_cron_tasks' => '',
|
|
'module_img_opti_wm_type' => 'image',
|
|
'module_img_opti_wm_image' => '',
|
|
'module_img_opti_wm_text_val' => '',
|
|
'module_img_opti_wm_position' => '5',
|
|
'module_img_opti_wm_opacity' => '50',
|
|
'module_img_opti_wm_categories' => array(),
|
|
'module_img_opti_wm_brands' => array(),
|
|
'module_img_opti_wm_target_product' => 1,
|
|
'module_img_opti_wm_target_category' => 0,
|
|
'module_img_opti_wm_target_brand' => 0,
|
|
'module_img_opti_wm_target_banner' => 0,
|
|
'module_img_opti_wm_target_blog' => 0,
|
|
'module_img_opti_wm_angle' => 0,
|
|
'module_img_opti_wm_corner_radius' => 0,
|
|
'module_img_opti_wm_size_type' => 'original',
|
|
'module_img_opti_wm_size_percent' => 20,
|
|
'module_img_opti_wm_text_color' => '#ffffff',
|
|
'module_img_opti_wm_text_font' => '',
|
|
'module_img_opti_wm_filter_mode' => 'exclude',
|
|
'module_img_opti_min_width' => '300',
|
|
'module_img_opti_min_height' => '300',
|
|
'module_img_opti_ph_product' => '',
|
|
'module_img_opti_ph_category' => '',
|
|
'module_img_opti_ph_brand' => '',
|
|
'module_img_opti_ph_target_product' => 1,
|
|
'module_img_opti_ph_target_category' => 1,
|
|
'module_img_opti_ph_target_brand' => 1,
|
|
'module_img_opti_sz_translit' => 1,
|
|
'module_img_opti_sz_spaces' => 1,
|
|
'module_img_opti_sz_special' => 1,
|
|
'module_img_opti_sz_lowercase_name' => 1,
|
|
'module_img_opti_sz_lowercase_ext' => 1,
|
|
'module_img_opti_sz_normalize_ext' => 1,
|
|
'module_img_opti_webp_on_fly' => 0,
|
|
'module_img_opti_lazy_load' => 0,
|
|
'module_img_opti_broken_fallback' => 0,
|
|
'module_img_opti_fallback_image' => '',
|
|
'module_img_opti_log_level' => 'changed',
|
|
'module_img_opti_min_savings_bytes' => 100,
|
|
'module_img_opti_min_savings_percent' => 0.3,
|
|
'module_img_opti_folder_rules' => array(),
|
|
'module_img_opti_heavy_limit' => 50,
|
|
'module_img_opti_heavy_min_size' => 500,
|
|
'module_img_opti_png_jpg_limit' => 100,
|
|
'module_img_opti_png_jpg_min_size' => 500,
|
|
'module_img_opti_sanitizer_limit' => 100,
|
|
'module_img_opti_disk_quota_total' => 0,
|
|
'module_img_opti_disk_quota_used' => 0,
|
|
'module_img_opti_disk_status' => 0,
|
|
'module_img_opti_wm_max_width' => 0,
|
|
'module_img_opti_wm_max_height' => 0,
|
|
'module_img_opti_truefilemanager_webp'=> (is_file(DIR_APPLICATION . 'controller/common/truefilemanager.php') || is_file(DIR_SYSTEM . 'library/sitecreator/trueFileManager/ControllerCommonTrueFileManager.php')) ? 1 : 0
|
|
);
|
|
|
|
foreach ($fields as $key => $default) {
|
|
if (isset($this->request->post[$key])) {
|
|
$data[$key] = $this->request->post[$key];
|
|
} else {
|
|
$data[$key] = ($this->config->get($key) !== null) ? $this->config->get($key) : $default;
|
|
}
|
|
}
|
|
|
|
$data['module_img_opti_wm_rules_json'] = $data['module_img_opti_wm_rules'];
|
|
$data['module_img_opti_ph_rules_json'] = $data['module_img_opti_ph_rules'];
|
|
$data['module_img_opti_cron_tasks_json'] = $data['module_img_opti_cron_tasks'];
|
|
|
|
if (empty($data['module_img_opti_cron_tasks'])) {
|
|
$default_cron_tasks = array(
|
|
array(
|
|
'status' => 0,
|
|
'name' => 'Default Task',
|
|
'new_only' => (int)$data['module_img_opti_cron_new_only'],
|
|
'entities' => $data['module_img_opti_cron_entities'],
|
|
'folder_mode' => $data['module_img_opti_cron_folder_mode'],
|
|
'folders' => $data['module_img_opti_cron_folders'],
|
|
'quality_override' => (int)$data['module_img_opti_cron_quality_override'],
|
|
'jpg_quality' => (int)$data['module_img_opti_cron_jpg_quality'],
|
|
'webp_quality' => (int)$data['module_img_opti_cron_webp_quality'],
|
|
'max_width' => (int)$data['module_img_opti_cron_max_width'],
|
|
'max_height' => (int)$data['module_img_opti_cron_max_height']
|
|
)
|
|
);
|
|
$data['module_img_opti_cron_tasks_json'] = json_encode($default_cron_tasks, JSON_UNESCAPED_UNICODE);
|
|
}
|
|
|
|
if (empty($data['module_img_opti_wm_rules'])) {
|
|
$default_wm_rules = array();
|
|
if ($data['module_img_opti_wm_type']) {
|
|
$categories_data = array();
|
|
$this->load->model('catalog/category');
|
|
foreach ((array)$data['module_img_opti_wm_categories'] as $c_id) {
|
|
$c_info = $this->model_catalog_category->getCategory($c_id);
|
|
if ($c_info) {
|
|
$categories_data[] = array('id' => (int)$c_id, 'name' => $c_info['name']);
|
|
}
|
|
}
|
|
|
|
$brands_data = array();
|
|
$this->load->model('catalog/manufacturer');
|
|
foreach ((array)$data['module_img_opti_wm_brands'] as $b_id) {
|
|
$b_info = $this->model_catalog_manufacturer->getManufacturer($b_id);
|
|
if ($b_info) {
|
|
$brands_data[] = array('id' => (int)$b_id, 'name' => $b_info['name']);
|
|
}
|
|
}
|
|
|
|
$default_wm_rules[] = array(
|
|
'status' => 1,
|
|
'name' => 'Default Rule',
|
|
'type' => $data['module_img_opti_wm_type'],
|
|
'image' => $data['module_img_opti_wm_image'],
|
|
'text_val' => $data['module_img_opti_wm_text_val'],
|
|
'position' => $data['module_img_opti_wm_position'],
|
|
'opacity' => $data['module_img_opti_wm_opacity'],
|
|
'size_type' => $data['module_img_opti_wm_size_type'],
|
|
'size_percent' => $data['module_img_opti_wm_size_percent'],
|
|
'angle' => $data['module_img_opti_wm_angle'],
|
|
'corner_radius' => $data['module_img_opti_wm_corner_radius'],
|
|
'text_font' => $data['module_img_opti_wm_text_font'],
|
|
'text_color' => $data['module_img_opti_wm_text_color'],
|
|
'target_product' => (int)$data['module_img_opti_wm_target_product'],
|
|
'target_category' => (int)$data['module_img_opti_wm_target_category'],
|
|
'target_brand' => (int)$data['module_img_opti_wm_target_brand'],
|
|
'target_banner' => (int)$data['module_img_opti_wm_target_banner'],
|
|
'target_blog' => (int)$data['module_img_opti_wm_target_blog'],
|
|
'filter_mode' => $data['module_img_opti_wm_filter_mode'],
|
|
'categories' => $categories_data,
|
|
'brands' => $brands_data
|
|
);
|
|
}
|
|
$data['module_img_opti_wm_rules_json'] = json_encode($default_wm_rules);
|
|
}
|
|
|
|
if (empty($data['module_img_opti_ph_rules'])) {
|
|
$default_ph_rules = array();
|
|
if ($data['module_img_opti_ph_product']) {
|
|
$default_ph_rules[] = array(
|
|
'status' => 1,
|
|
'name' => 'Products Placeholder',
|
|
'target_product' => 1,
|
|
'target_category' => 0,
|
|
'target_brand' => 0,
|
|
'image' => $data['module_img_opti_ph_product'],
|
|
'filter_mode' => 'exclude',
|
|
'categories' => array(),
|
|
'brands' => array()
|
|
);
|
|
}
|
|
if ($data['module_img_opti_ph_category']) {
|
|
$default_ph_rules[] = array(
|
|
'status' => 1,
|
|
'name' => 'Categories Placeholder',
|
|
'target_product' => 0,
|
|
'target_category' => 1,
|
|
'target_brand' => 0,
|
|
'image' => $data['module_img_opti_ph_category'],
|
|
'filter_mode' => 'exclude',
|
|
'categories' => array(),
|
|
'brands' => array()
|
|
);
|
|
}
|
|
if ($data['module_img_opti_ph_brand']) {
|
|
$default_ph_rules[] = array(
|
|
'status' => 1,
|
|
'name' => 'Brands Placeholder',
|
|
'target_product' => 0,
|
|
'target_category' => 0,
|
|
'target_brand' => 1,
|
|
'image' => $data['module_img_opti_ph_brand'],
|
|
'filter_mode' => 'exclude',
|
|
'categories' => array(),
|
|
'brands' => array()
|
|
);
|
|
}
|
|
$data['module_img_opti_ph_rules_json'] = json_encode($default_ph_rules);
|
|
}
|
|
|
|
foreach (array('module_img_opti_cron_folders', 'module_img_opti_cron_exclude_folders', 'module_img_opti_cron_entities') as $arr_key) {
|
|
if (isset($data[$arr_key]) && !is_array($data[$arr_key])) {
|
|
if (is_string($data[$arr_key])) {
|
|
if (strpos($data[$arr_key], '[') === 0 || strpos($data[$arr_key], '{') === 0) {
|
|
$decoded = json_decode(html_entity_decode($data[$arr_key], ENT_QUOTES, 'UTF-8'), true);
|
|
$data[$arr_key] = is_array($decoded) ? $decoded : array();
|
|
} else {
|
|
$data[$arr_key] = explode(',', $data[$arr_key]);
|
|
}
|
|
} else {
|
|
$data[$arr_key] = array();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($data['module_img_opti_folder_rules'])) {
|
|
if (!is_array($data['module_img_opti_folder_rules'])) {
|
|
$rules = json_decode(html_entity_decode((string)$data['module_img_opti_folder_rules'], ENT_QUOTES, 'UTF-8'), true);
|
|
$data['module_img_opti_folder_rules'] = is_array($rules) ? $rules : array();
|
|
}
|
|
} else {
|
|
$data['module_img_opti_folder_rules'] = array();
|
|
}
|
|
|
|
$this->load->model('tool/image');
|
|
$data['placeholder'] = $this->model_tool_image->resize('no_image.png', 100, 100);
|
|
|
|
$image_fields = array('wm_image', 'ph_product', 'ph_category', 'ph_brand', 'fallback_image');
|
|
foreach ($image_fields as $img_field) {
|
|
$cfg_val = $data['module_img_opti_' . $img_field];
|
|
if (is_string($cfg_val) && is_file(DIR_IMAGE . $cfg_val)) {
|
|
$data['thumb_' . $img_field] = $this->model_tool_image->resize($cfg_val, 100, 100);
|
|
} else {
|
|
$data['thumb_' . $img_field] = $data['placeholder'];
|
|
}
|
|
}
|
|
|
|
$this->load->model('catalog/category');
|
|
$data['wm_categories'] = array();
|
|
if (!empty($data['module_img_opti_wm_categories'])) {
|
|
foreach ($data['module_img_opti_wm_categories'] as $category_id) {
|
|
$category_info = $this->model_catalog_category->getCategory((int)$category_id);
|
|
if ($category_info) {
|
|
$data['wm_categories'][] = array(
|
|
'category_id' => $category_info['category_id'],
|
|
'name' => ($category_info['path']) ? $category_info['path'] . ' > ' . $category_info['name'] : $category_info['name']
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->load->model('catalog/manufacturer');
|
|
$data['wm_brands'] = array();
|
|
if (!empty($data['module_img_opti_wm_brands'])) {
|
|
foreach ($data['module_img_opti_wm_brands'] as $manufacturer_id) {
|
|
$manufacturer_info = $this->model_catalog_manufacturer->getManufacturer((int)$manufacturer_id);
|
|
if ($manufacturer_info) {
|
|
$data['wm_brands'][] = array(
|
|
'manufacturer_id' => $manufacturer_info['manufacturer_id'],
|
|
'name' => $manufacturer_info['name']
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$data['user_token'] = $this->session->data['user_token'];
|
|
$data['catalog_url'] = (defined('HTTPS_CATALOG') && HTTPS_CATALOG) ? HTTPS_CATALOG : (defined('HTTP_CATALOG') ? HTTP_CATALOG : '');
|
|
$data['is_licensed'] = $this->checkLicense($data['module_img_opti_token']);
|
|
$data['catalog_dir'] = DIR_IMAGE . 'catalog';
|
|
|
|
$data['catalog_items'] = array();
|
|
$data['cron_folders_list'] = array();
|
|
$base_dir = DIR_IMAGE . 'catalog/';
|
|
|
|
if ($data['is_licensed'] && is_dir($base_dir)) {
|
|
$data['cron_folders_list'][] = 'catalog';
|
|
$cached_tree = $this->cache->get('img_opti_cron_folders_tree');
|
|
if (is_array($cached_tree)) {
|
|
$tree = $cached_tree;
|
|
} else {
|
|
$tree = $this->getFoldersTree($base_dir, 'catalog');
|
|
$this->cache->set('img_opti_cron_folders_tree', $tree);
|
|
}
|
|
$data['cron_folders_list'] = array_merge($data['cron_folders_list'], $tree);
|
|
|
|
$items = array_diff(scandir($base_dir), array('.', '..'));
|
|
foreach ($items as $item) {
|
|
if (is_dir($base_dir . $item)) {
|
|
$data['catalog_items'][] = array('type' => 'folder', 'name' => $item, 'path' => 'catalog/' . $item);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($this->config->get('module_img_opti_cron_folders') === null && !isset($this->request->post['module_img_opti_cron_folders'])) {
|
|
$data['module_img_opti_cron_folders'] = array();
|
|
}
|
|
|
|
$open_basedir = ini_get('open_basedir');
|
|
|
|
$data['ttf_fonts'] = array();
|
|
$font_dirs = array(
|
|
DIR_SYSTEM . 'library/font/',
|
|
DIR_SYSTEM . 'fonts/',
|
|
DIR_SYSTEM . 'font/',
|
|
DIR_CATALOG . 'view/theme/default/fonts/'
|
|
);
|
|
if (DIRECTORY_SEPARATOR === '/') {
|
|
if (!$open_basedir) {
|
|
$font_dirs[] = '/usr/share/fonts/truetype/dejavu/';
|
|
$font_dirs[] = '/usr/share/fonts/truetype/liberation/';
|
|
$font_dirs[] = '/usr/share/fonts/truetype/freefont/';
|
|
}
|
|
} else {
|
|
if (!$open_basedir) {
|
|
$font_dirs[] = 'C:/Windows/Fonts/';
|
|
}
|
|
}
|
|
$allowed_dirs = array();
|
|
if ($open_basedir) {
|
|
$allowed_dirs = explode(PATH_SEPARATOR, $open_basedir);
|
|
}
|
|
$font_added = array();
|
|
foreach ($font_dirs as $dir) {
|
|
$is_allowed = true;
|
|
if ($open_basedir) {
|
|
$is_allowed = false;
|
|
$norm_dir = rtrim(str_replace('\\', '/', $dir), '/');
|
|
foreach ($allowed_dirs as $allowed) {
|
|
$norm_allowed = rtrim(str_replace('\\', '/', $allowed), '/');
|
|
if ($norm_allowed && (strpos($norm_dir, $norm_allowed) === 0 || strpos($norm_allowed, $norm_dir) === 0)) {
|
|
$is_allowed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!$is_allowed) {
|
|
continue;
|
|
}
|
|
if (@is_dir($dir)) {
|
|
$files = @glob($dir . '*.ttf');
|
|
if ($files) {
|
|
$count = 0;
|
|
foreach ($files as $file) {
|
|
$name = $this->mb_basename($file);
|
|
if (!in_array($name, $font_added)) {
|
|
$data['ttf_fonts'][] = array(
|
|
'name' => $name,
|
|
'path' => $file
|
|
);
|
|
$font_added[] = $name;
|
|
$count++;
|
|
if ($count > 30) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$data['header'] = $this->load->controller('common/header');
|
|
$data['column_left'] = $this->load->controller('common/column_left');
|
|
$data['footer'] = $this->load->controller('common/footer');
|
|
|
|
$this->response->setOutput($this->load->view('module/img_opti', $data));
|
|
}
|
|
|
|
public function autocomplete_category() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (isset($this->request->get['filter_name'])) {
|
|
$this->load->model('catalog/category');
|
|
|
|
$filter_data = array(
|
|
'filter_name' => $this->request->get['filter_name'],
|
|
'sort' => 'name',
|
|
'order' => 'ASC',
|
|
'start' => 0,
|
|
'limit' => 15
|
|
);
|
|
|
|
$results = $this->model_catalog_category->getCategories($filter_data);
|
|
|
|
foreach ($results as $result) {
|
|
$json[] = array(
|
|
'category_id' => $result['category_id'],
|
|
'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function autocomplete_manufacturer() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (isset($this->request->get['filter_name'])) {
|
|
$this->load->model('catalog/manufacturer');
|
|
|
|
$filter_data = array(
|
|
'filter_name' => $this->request->get['filter_name'],
|
|
'start' => 0,
|
|
'limit' => 15
|
|
);
|
|
|
|
$results = $this->model_catalog_manufacturer->getManufacturers($filter_data);
|
|
|
|
foreach ($results as $result) {
|
|
$json[] = array(
|
|
'manufacturer_id' => $result['manufacturer_id'],
|
|
'name' => strip_tags(html_entity_decode($result['name'], ENT_QUOTES, 'UTF-8'))
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function validate() {
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$this->error['warning'] = $this->language->get('error_permission');
|
|
}
|
|
return !$this->error;
|
|
}
|
|
|
|
private function getToken() {
|
|
if (!empty($this->request->post['module_token'])) {
|
|
return trim($this->request->post['module_token']);
|
|
}
|
|
if (!empty($this->request->post['module_img_opti_token'])) {
|
|
return trim($this->request->post['module_img_opti_token']);
|
|
}
|
|
return $this->config->get('module_img_opti_token');
|
|
}
|
|
|
|
public function scan() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$selected_items = isset($this->request->post['selected_items']) ? (array)$this->request->post['selected_items'] : array();
|
|
$allFiles = $this->scanDirectoryHelper($selected_items);
|
|
|
|
file_put_contents(DIR_CACHE . 'img_opti_queue.json', json_encode($allFiles, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
|
$json['total'] = count($allFiles);
|
|
$json['success'] = $this->language->get('text_found') . $json['total'];
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function process() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array('log' => array(), 'done' => 0, 'finished' => false, 'batch_old' => 0, 'batch_new' => 0);
|
|
$queueFile = DIR_CACHE . 'img_opti_queue.json';
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
if (!file_exists($queueFile)) {
|
|
$json['finished'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$fp = fopen($queueFile, 'c+');
|
|
if (!$fp) {
|
|
$json['finished'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
flock($fp, LOCK_EX);
|
|
|
|
$filesize = filesize($queueFile);
|
|
$queue = array();
|
|
if ($filesize > 0) {
|
|
$content = fread($fp, $filesize);
|
|
$queue = json_decode($content, true);
|
|
}
|
|
|
|
if (empty($queue)) {
|
|
flock($fp, LOCK_UN);
|
|
fclose($fp);
|
|
@unlink($queueFile);
|
|
$json['finished'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$batch = array();
|
|
$batchSize = isset($this->request->post['batch_size']) ? (int)$this->request->post['batch_size'] : 200;
|
|
for ($i = 0; $i < $batchSize; $i++) {
|
|
if (empty($queue)) break;
|
|
$batch[] = array_shift($queue);
|
|
}
|
|
|
|
ftruncate($fp, 0);
|
|
rewind($fp);
|
|
fwrite($fp, json_encode($queue, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
|
fflush($fp);
|
|
flock($fp, LOCK_UN);
|
|
fclose($fp);
|
|
|
|
$sourceDir = DIR_IMAGE . 'catalog';
|
|
$mode = isset($this->request->post['mode']) ? preg_replace('/[^a-z]/', '', (string)$this->request->post['mode']) : 'copy';
|
|
$copyFolder = !empty($this->request->post['copy_folder']) ? preg_replace('/[^a-zA-Z0-9_-]/', '', $this->request->post['copy_folder']) : 'catalog_new';
|
|
$engine = $this->config->get('module_img_opti_engine') == 'imagick' && extension_loaded('imagick') ? 'imagick' : 'gd';
|
|
$this->load->model('tool/image');
|
|
|
|
if ($mode == 'copy') {
|
|
$this->registerModuleFolder($copyFolder);
|
|
}
|
|
|
|
$targetDir = ($mode == 'copy') ? DIR_IMAGE . $copyFolder : $sourceDir;
|
|
|
|
$maxWidth = isset($this->request->post['max_width']) ? (int)$this->request->post['max_width'] : 1600;
|
|
$maxHeight = isset($this->request->post['max_height']) ? (int)$this->request->post['max_height'] : 1600;
|
|
$jpgQ = isset($this->request->post['jpg_quality']) ? (int)$this->request->post['jpg_quality'] : 72;
|
|
$pngQ = isset($this->request->post['png_quality']) ? (int)$this->request->post['png_quality'] : 7;
|
|
$webpQ = isset($this->request->post['webp_quality']) ? (int)$this->request->post['webp_quality'] : 80;
|
|
$threshold = isset($this->request->post['size_threshold']) ? (float)$this->request->post['size_threshold'] : 0;
|
|
$min_savings_bytes = isset($this->request->post['module_img_opti_min_savings_bytes']) ? (int)$this->request->post['module_img_opti_min_savings_bytes'] : 100;
|
|
$min_savings_percent = isset($this->request->post['module_img_opti_min_savings_percent']) ? (float)$this->request->post['module_img_opti_min_savings_percent'] : 0.3;
|
|
$log_level = isset($this->request->post['module_img_opti_log_level']) ? (string)$this->request->post['module_img_opti_log_level'] : (isset($this->request->post['log_level']) ? (string)$this->request->post['log_level'] : 'changed');
|
|
|
|
$processed = 0;
|
|
$batchOptimized = 0;
|
|
$batchOldSize = 0;
|
|
$batchNewSize = 0;
|
|
|
|
$dirImageNorm = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/';
|
|
|
|
$cache_updates = array();
|
|
foreach ($batch as $filePath) {
|
|
$realFilePath = realpath($filePath);
|
|
if (!$realFilePath || strpos(str_replace('\\', '/', $realFilePath), $dirImageNorm) !== 0 || !is_file($realFilePath)) continue;
|
|
|
|
$pathNorm = str_replace('\\', '/', $realFilePath);
|
|
$relativePath = preg_replace('/^' . preg_quote($dirImageNorm, '/') . '/i', '', $pathNorm);
|
|
$relativePath = ltrim($relativePath, '/');
|
|
|
|
$cleanRel = preg_replace('/^catalog\//i', '', $relativePath);
|
|
$cleanRel = ltrim($cleanRel, '/');
|
|
|
|
$targetDirNorm = rtrim(str_replace('\\', '/', $targetDir), '/');
|
|
$destPath = $targetDirNorm . '/' . $cleanRel;
|
|
|
|
if (!is_dir($this->mb_dirname($destPath))) {
|
|
@mkdir($this->mb_dirname($destPath), 0755, true);
|
|
}
|
|
|
|
$oldSize = filesize($realFilePath);
|
|
$tempPath = DIR_CACHE . 'opti_temp_' . md5($realFilePath);
|
|
|
|
$fileParams = $this->getImageParameters($relativePath, array(
|
|
'module_img_opti_folder_rules' => $this->config->get('module_img_opti_folder_rules'),
|
|
'module_img_opti_jpg_quality' => $jpgQ,
|
|
'module_img_opti_png_quality' => $pngQ,
|
|
'module_img_opti_webp_quality' => $webpQ,
|
|
'module_img_opti_max_width' => $maxWidth,
|
|
'module_img_opti_max_height' => $maxHeight
|
|
));
|
|
|
|
if ($this->optimizeImage($realFilePath, $tempPath, $fileParams['max_width'], $fileParams['max_height'], $fileParams['jpg_quality'], $fileParams['png_quality'], $fileParams['webp_quality'], $engine)) {
|
|
clearstatcache();
|
|
$newSize = filesize($tempPath);
|
|
|
|
$increasePercent = ($oldSize > 0) ? (($newSize - $oldSize) / $oldSize) * 100 : 0;
|
|
$statusClass = 'text-success';
|
|
$sign = $increasePercent > 0 ? '+' : '';
|
|
$statusText = $this->language->get('text_compressed') . ' (' . $sign . round($increasePercent, 1) . '%)';
|
|
|
|
$file_ok = true;
|
|
$savedBytes = $oldSize - $newSize;
|
|
$savedPercent = ($oldSize > 0) ? ($savedBytes / $oldSize) * 100 : 0;
|
|
$is_too_small_gain = ($savedBytes < $min_savings_bytes || $savedPercent < $min_savings_percent);
|
|
|
|
if ($increasePercent > $threshold || $is_too_small_gain) {
|
|
@unlink($tempPath);
|
|
$copied = true;
|
|
if ($mode == 'copy' || $realFilePath !== realpath($destPath)) {
|
|
$copied = @copy($realFilePath, $destPath);
|
|
}
|
|
if (!$copied) {
|
|
$file_ok = false;
|
|
$statusClass = 'text-danger';
|
|
$statusText = $this->language->get('text_error_process') . ' (Failed to copy original)';
|
|
} else {
|
|
$newSize = $oldSize;
|
|
$statusClass = 'text-muted';
|
|
$statusText = $this->language->get('text_skipped_no_gain') ? $this->language->get('text_skipped_no_gain') : 'Skipped';
|
|
$cacheKey = $this->getRelativeImagePath($realFilePath);
|
|
$cache_updates[$cacheKey] = array(
|
|
'size' => $oldSize,
|
|
'mtime' => @filemtime($realFilePath)
|
|
);
|
|
}
|
|
} else {
|
|
if (file_exists($destPath)) {
|
|
@unlink($destPath);
|
|
}
|
|
$moved = false;
|
|
if (@rename($tempPath, $destPath)) {
|
|
$moved = true;
|
|
} else {
|
|
if (@copy($tempPath, $destPath)) {
|
|
@unlink($tempPath);
|
|
$moved = true;
|
|
}
|
|
}
|
|
if (!$moved) {
|
|
$file_ok = false;
|
|
$statusClass = 'text-danger';
|
|
$statusText = $this->language->get('text_error_process') . ' (Failed to write optimized file)';
|
|
} else {
|
|
clearstatcache(true, $destPath);
|
|
$cacheKey = $this->getRelativeImagePath($realFilePath);
|
|
$cache_updates[$cacheKey] = array(
|
|
'size' => @filesize($destPath),
|
|
'mtime' => ($mode == 'replace') ? @filemtime($destPath) : @filemtime($realFilePath)
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($file_ok) {
|
|
$batchOldSize += $oldSize;
|
|
$batchNewSize += $newSize;
|
|
}
|
|
|
|
$displayPath = $this->safe_utf8($cleanRel);
|
|
$isUnchanged = ($statusClass === 'text-muted');
|
|
if ($file_ok && !$isUnchanged) {
|
|
$batchOptimized++;
|
|
}
|
|
if (!$isUnchanged || $log_level === 'all') {
|
|
$compareBtn = '';
|
|
if (!$isUnchanged) {
|
|
$imgSize = @getimagesize($realFilePath);
|
|
if ($imgSize && $imgSize[0] >= 150 && $imgSize[1] >= 150) {
|
|
$compareBtn = sprintf(
|
|
'<button type="button" class="btn btn-default btn-xs btn-compare-opti" data-path="%s" style="margin-left:10px;" title="%s"><i class="fa fa-balance-scale"></i> %s</button>',
|
|
htmlspecialchars($relativePath, ENT_QUOTES, 'UTF-8'),
|
|
$this->translate('text_compare', 'Compare'),
|
|
$this->translate('text_compare', 'Compare')
|
|
);
|
|
}
|
|
}
|
|
$json['log'][] = sprintf(
|
|
"<div style='border-bottom:1px solid #333; padding:4px 0;'><span class='%s'>[%s] %s</span> <small class='text-muted'>(%s → %s)</small>%s</div>",
|
|
$statusClass,
|
|
$statusText,
|
|
htmlspecialchars('image/catalog/' . $displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8'),
|
|
$this->formatBytes($oldSize),
|
|
$this->formatBytes($newSize),
|
|
$isUnchanged ? '' : $compareBtn
|
|
);
|
|
}
|
|
} else {
|
|
$displayPath = $this->safe_utf8($cleanRel);
|
|
$json['log'][] = sprintf(
|
|
"<div style='border-bottom:1px solid #333; padding:4px 0;'><span class='text-danger'>%s %s</span></div>",
|
|
$this->language->get('text_error_process'),
|
|
htmlspecialchars('image/catalog/' . $displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8')
|
|
);
|
|
}
|
|
$processed++;
|
|
gc_collect_cycles();
|
|
|
|
$memLimit = $this->getMemoryLimitInBytesHelper();
|
|
if (memory_get_usage(true) > 0.85 * $memLimit) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!empty($cache_updates)) {
|
|
$cache = $this->getOptimizedCache();
|
|
foreach ($cache_updates as $k => $v) {
|
|
$cache[$k] = $v;
|
|
}
|
|
$this->saveOptimizedCache($cache);
|
|
}
|
|
|
|
$json['done'] = $processed;
|
|
$json['optimized'] = $batchOptimized;
|
|
$json['batch_old'] = $batchOldSize;
|
|
$json['batch_new'] = $batchNewSize;
|
|
|
|
if (!file_exists($queueFile)) {
|
|
$json['finished'] = true;
|
|
}
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function applyChanges() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$copyFolder = !empty($this->request->post['copy_folder']) ? preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$this->request->post['copy_folder']) : 'catalog_new';
|
|
if (empty($copyFolder) || $copyFolder === 'catalog') {
|
|
$json['error'] = $this->language->get('error_invalid_folder');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$source_dir = realpath(DIR_IMAGE . $copyFolder);
|
|
$target_dir = realpath(DIR_IMAGE . 'catalog');
|
|
$realImageDir = realpath(DIR_IMAGE);
|
|
|
|
if (!$source_dir || strpos($source_dir, $realImageDir) !== 0 || !is_dir($source_dir)) {
|
|
$json['error'] = $this->language->get('text_error_source');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$backup_name = 'backup_' . date('Ymd_His');
|
|
$backup_dir = DIR_IMAGE . $backup_name;
|
|
|
|
$do_backup = isset($this->request->post['backup']) && (int)$this->request->post['backup'] === 1;
|
|
|
|
if ($do_backup) {
|
|
$this->registerModuleFolder($backup_name);
|
|
if (!is_dir($backup_dir)) @mkdir($backup_dir, 0755, true);
|
|
$backup_dir = realpath($backup_dir);
|
|
}
|
|
|
|
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source_dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
|
|
$errors = array();
|
|
$cache_updates = array();
|
|
|
|
foreach ($files as $file) {
|
|
$currentPath = str_replace('\\', '/', $file->getPathname());
|
|
$relative_path = preg_replace('/^' . preg_quote(str_replace('\\', '/', $source_dir), '/') . '/i', '', $currentPath);
|
|
$relative_path = ltrim($relative_path, '/');
|
|
|
|
$dest_file = rtrim(str_replace('\\', '/', $target_dir), '/') . '/' . $relative_path;
|
|
$dest_folder = $this->mb_dirname($dest_file);
|
|
|
|
if ($file->isDir()) {
|
|
if (!is_dir($dest_folder)) {
|
|
if (!@mkdir($dest_folder, 0755, true)) {
|
|
$errors[] = 'Failed to create directory: ' . $relative_path;
|
|
}
|
|
}
|
|
} else {
|
|
if (!is_dir($dest_folder)) {
|
|
@mkdir($dest_folder, 0755, true);
|
|
}
|
|
|
|
$replaced = true;
|
|
if (file_exists($dest_file)) {
|
|
if ($do_backup) {
|
|
$back_file = rtrim(str_replace('\\', '/', $backup_dir), '/') . '/' . $relative_path;
|
|
if (!is_dir($this->mb_dirname($back_file))) @mkdir($this->mb_dirname($back_file), 0755, true);
|
|
if (file_exists($back_file)) {
|
|
@unlink($back_file);
|
|
}
|
|
if (!@rename($dest_file, $back_file)) {
|
|
if (!@copy($dest_file, $back_file)) {
|
|
$replaced = false;
|
|
$errors[] = 'Backup failed: ' . $relative_path;
|
|
} else {
|
|
@unlink($dest_file);
|
|
}
|
|
}
|
|
} else {
|
|
if (!@unlink($dest_file)) {
|
|
$replaced = false;
|
|
$errors[] = 'Failed to delete original: ' . $relative_path;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($replaced) {
|
|
$moved = false;
|
|
if (@rename($file->getPathname(), $dest_file)) {
|
|
$moved = true;
|
|
} else {
|
|
if (@copy($file->getPathname(), $dest_file)) {
|
|
@unlink($file->getPathname());
|
|
$moved = true;
|
|
}
|
|
}
|
|
if (!$moved) {
|
|
$errors[] = 'Failed to apply changes: ' . $relative_path;
|
|
} else {
|
|
clearstatcache(true, $dest_file);
|
|
$cacheKey = $this->getRelativeImagePath($dest_file);
|
|
$cache_updates[$cacheKey] = array(
|
|
'size' => @filesize($dest_file),
|
|
'mtime' => @filemtime($dest_file)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($cache_updates)) {
|
|
$cache = $this->getOptimizedCache();
|
|
foreach ($cache_updates as $k => $v) {
|
|
$cache[$k] = $v;
|
|
}
|
|
$this->saveOptimizedCache($cache);
|
|
}
|
|
|
|
$this->recursiveDelete($source_dir);
|
|
|
|
$msg = $this->language->get('text_apply_success');
|
|
$msg .= $do_backup ? $this->language->get('text_apply_backup_ok') . htmlspecialchars($backup_name, ENT_QUOTES, 'UTF-8') : $this->language->get('text_apply_no_backup');
|
|
|
|
if (!empty($errors)) {
|
|
$json['success'] = $msg . '<br><span class="text-danger">' . $this->language->get('text_apply_errors_warning') . '<br>' . implode('<br>', array_slice($errors, 0, 10)) . (count($errors) > 10 ? '<br>...' : '') . '</span>';
|
|
} else {
|
|
$json['success'] = $msg;
|
|
}
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function resetCache() {
|
|
$this->loadLanguageSafe();
|
|
$json = array();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
$cache_file = DIR_CACHE . 'img_opti_optimized.json';
|
|
if (file_exists($cache_file)) {
|
|
@unlink($cache_file);
|
|
}
|
|
$this->cache->delete('img_opti_cron_folders_tree');
|
|
$json['success'] = $this->translate('text_cache_cleared', 'Optimization cache cleared. Next scan will check all files.');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function clearTrash() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$query = $this->db->query("SELECT `value` FROM `" . DB_PREFIX . "setting` WHERE `code` = 'module_img_opti' AND `key` = 'module_img_opti_folders'");
|
|
$folders = ($query->num_rows && $query->row['value']) ? json_decode($query->row['value'], true) : array();
|
|
|
|
$folders[] = 'catalog_trash';
|
|
$folders = array_unique($folders);
|
|
|
|
$realImageDir = realpath(DIR_IMAGE);
|
|
|
|
foreach ($folders as $folder) {
|
|
$folder = preg_replace('/[^a-zA-Z0-9_\-\/]/', '', (string)$folder);
|
|
if (empty($folder) || $folder === 'catalog') continue;
|
|
|
|
$path = realpath(DIR_IMAGE . $folder);
|
|
if ($path && strpos($path, $realImageDir) === 0 && is_dir($path)) {
|
|
$this->recursiveDelete($path);
|
|
}
|
|
}
|
|
|
|
$compare_path = realpath(DIR_IMAGE . 'cache/compare');
|
|
if ($compare_path && strpos($compare_path, $realImageDir) === 0 && is_dir($compare_path)) {
|
|
$this->recursiveDelete($compare_path);
|
|
}
|
|
$cache_file = DIR_CACHE . 'img_opti_optimized.json';
|
|
if (file_exists($cache_file)) {
|
|
@unlink($cache_file);
|
|
}
|
|
$this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE `code` = 'module_img_opti' AND `key` = 'module_img_opti_folders'");
|
|
|
|
$json['success'] = $this->language->get('text_quarantine_empty');
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function clear_backups() {
|
|
$this->loadLanguageSafe();
|
|
$json = array();
|
|
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$backup_dir = realpath(DIR_IMAGE . 'catalog_trash/backup');
|
|
$realImageDir = realpath(DIR_IMAGE);
|
|
|
|
if ($backup_dir && strpos($backup_dir, $realImageDir) === 0 && is_dir($backup_dir)) {
|
|
$this->recursiveDelete($backup_dir);
|
|
$json['success'] = $this->language->get('text_backups_cleared');
|
|
} else {
|
|
$json['error'] = $this->language->get('error_no_backups');
|
|
}
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function download_folder() {
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
return;
|
|
}
|
|
|
|
$folder = isset($this->request->get['folder']) ? preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$this->request->get['folder']) : '';
|
|
if (empty($folder) || $folder === 'catalog') {
|
|
return;
|
|
}
|
|
|
|
$dir_path = realpath(DIR_IMAGE . $folder);
|
|
$realImageDir = realpath(DIR_IMAGE);
|
|
|
|
if (!$dir_path || strpos($dir_path, $realImageDir) !== 0 || !is_dir($dir_path)) {
|
|
return;
|
|
}
|
|
|
|
$zip_file = DIR_CACHE . $folder . '_' . date('Ymd_His') . '.zip';
|
|
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($zip_file, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
|
|
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir_path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
|
|
foreach ($files as $file) {
|
|
if ($file->isFile()) {
|
|
$filePath = $file->getPathname();
|
|
$relativePath = substr($filePath, strlen($dir_path) + 1);
|
|
$zip->addFile($filePath, $relativePath);
|
|
}
|
|
}
|
|
$zip->close();
|
|
}
|
|
|
|
if (file_exists($zip_file)) {
|
|
header('Content-Type: application/zip');
|
|
header('Content-Disposition: attachment; filename="' . $this->mb_basename($zip_file) . '"');
|
|
header('Content-Length: ' . filesize($zip_file));
|
|
readfile($zip_file);
|
|
@unlink($zip_file);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
public function compare_preview() {
|
|
$this->loadLanguageSafe();
|
|
$json = array();
|
|
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$id = isset($this->request->post['id']) ? str_replace(array('../', '..\\', "\0"), '', (string)$this->request->post['id']) : '';
|
|
$tool = isset($this->request->post['tool']) ? preg_replace('/[^a-zA-Z0-9_]/', '', (string)$this->request->post['tool']) : '';
|
|
|
|
$this->load->model('module/img_opti');
|
|
$json = $this->model_module_img_opti->generateCompareUrls($tool, $id);
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function registerModuleFolder($folder_name) {
|
|
$folder_name = preg_replace('/[^a-zA-Z0-9_\-\/]/', '', $folder_name);
|
|
if (empty($folder_name)) return;
|
|
|
|
$query = $this->db->query("SELECT `value` FROM `" . DB_PREFIX . "setting` WHERE `code` = 'module_img_opti' AND `key` = 'module_img_opti_folders'");
|
|
$folders = ($query->num_rows && $query->row['value']) ? json_decode($query->row['value'], true) : array();
|
|
|
|
if (!in_array($folder_name, $folders)) {
|
|
$folders[] = $folder_name;
|
|
$this->db->query("DELETE FROM `" . DB_PREFIX . "setting` WHERE `code` = 'module_img_opti' AND `key` = 'module_img_opti_folders'");
|
|
$this->db->query("INSERT INTO `" . DB_PREFIX . "setting` SET `store_id` = '0', `code` = 'module_img_opti', `key` = 'module_img_opti_folders', `value` = '" . $this->db->escape(json_encode($folders)) . "'");
|
|
}
|
|
}
|
|
|
|
private function recursiveDelete($dir) {
|
|
$realDir = realpath($dir);
|
|
if (!$realDir || strpos($realDir, realpath(DIR_IMAGE)) !== 0 || !is_dir($realDir)) return false;
|
|
|
|
$files = array_diff(scandir($realDir), array('.','..'));
|
|
foreach ($files as $file) {
|
|
$path = $realDir . DIRECTORY_SEPARATOR . $file;
|
|
(is_dir($path)) ? $this->recursiveDelete($path) : @unlink($path);
|
|
}
|
|
return @rmdir($realDir);
|
|
}
|
|
|
|
private function safe_utf8($str) {
|
|
$str = (string)$str;
|
|
if (mb_check_encoding($str, 'UTF-8')) {
|
|
return $str;
|
|
}
|
|
$converted = @mb_convert_encoding($str, 'UTF-8', 'Windows-1251');
|
|
return $converted ? $converted : $str;
|
|
}
|
|
|
|
private function scanDirectoryHelper($selected_items) {
|
|
$allFiles = array();
|
|
$scanRootFiles = false;
|
|
$dirImageNorm = rtrim(str_replace('\\', '/', DIR_IMAGE), '/');
|
|
$cache = $this->getOptimizedCache();
|
|
|
|
if (empty($selected_items) || in_array('all', (array)$selected_items)) {
|
|
$dirsToScan = array($dirImageNorm . '/catalog');
|
|
} else {
|
|
$dirsToScan = array();
|
|
if (in_array('root_files', (array)$selected_items)) {
|
|
$scanRootFiles = true;
|
|
}
|
|
foreach ((array)$selected_items as $item) {
|
|
if ($item === 'root_files' || $item === 'all') continue;
|
|
|
|
$clean_item = preg_replace('/[^a-zA-Z0-9_\-\/]/', '', (string)$item);
|
|
$clean_item = str_replace(array('../', '..\\'), '', $clean_item);
|
|
|
|
$fullPath = realpath($dirImageNorm . '/' . ltrim($clean_item, '/'));
|
|
if ($fullPath && strpos(str_replace('\\', '/', $fullPath), $dirImageNorm) === 0 && is_dir($fullPath)) {
|
|
$dirsToScan[] = str_replace('\\', '/', $fullPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($dirsToScan)) {
|
|
foreach ($dirsToScan as $dir) {
|
|
if (!is_dir($dir)) continue;
|
|
try {
|
|
$directory = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
|
|
$iterator = new RecursiveIteratorIterator($directory);
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile()) {
|
|
$ext = mb_strtolower($file->getExtension(), 'UTF-8');
|
|
if (in_array($ext, array('jpg', 'jpeg', 'png', 'webp', 'gif'))) {
|
|
$filePathNorm = str_replace('\\', '/', $file->getPathname());
|
|
$relPath = $this->getRelativeImagePath($filePathNorm);
|
|
$skip = false;
|
|
if (isset($cache[$relPath])) {
|
|
clearstatcache(true, $filePathNorm);
|
|
$size = @filesize($filePathNorm);
|
|
$mtime = @filemtime($filePathNorm);
|
|
if ($size === $cache[$relPath]['size'] && $mtime === $cache[$relPath]['mtime']) {
|
|
$skip = true;
|
|
}
|
|
}
|
|
if (!$skip) {
|
|
$allFiles[] = $filePathNorm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($scanRootFiles) {
|
|
$base_dir = $dirImageNorm . '/catalog/';
|
|
if (is_dir($base_dir)) {
|
|
$items = scandir($base_dir);
|
|
foreach ($items as $item) {
|
|
if ($item == '.' || $item == '..') continue;
|
|
$fullPath = $base_dir . $item;
|
|
if (is_file($fullPath)) {
|
|
$ext = mb_strtolower($this->mb_pathinfo($fullPath, PATHINFO_EXTENSION), 'UTF-8');
|
|
if (in_array($ext, array('jpg', 'jpeg', 'png', 'webp', 'gif'))) {
|
|
$filePathNorm = str_replace('\\', '/', $fullPath);
|
|
$relPath = $this->getRelativeImagePath($filePathNorm);
|
|
$skip = false;
|
|
if (isset($cache[$relPath])) {
|
|
clearstatcache(true, $filePathNorm);
|
|
$size = @filesize($filePathNorm);
|
|
$mtime = @filemtime($filePathNorm);
|
|
if ($size === $cache[$relPath]['size'] && $mtime === $cache[$relPath]['mtime']) {
|
|
$skip = true;
|
|
}
|
|
}
|
|
if (!$skip) {
|
|
$allFiles[] = $filePathNorm;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return array_unique($allFiles);
|
|
}
|
|
|
|
private function optimizeImage($src, $dest, $maxWidth, $maxHeight, $jpgQuality, $pngCompression, $webpQuality, $engine = 'gd') {
|
|
if ($engine == 'imagick' && extension_loaded('imagick')) {
|
|
try {
|
|
$img = new Imagick($src);
|
|
$img->transformImageColorspace(Imagick::COLORSPACE_SRGB);
|
|
|
|
$width = $img->getImageWidth();
|
|
$height = $img->getImageHeight();
|
|
$ext = strtolower($this->mb_pathinfo($src, PATHINFO_EXTENSION));
|
|
|
|
if (($maxWidth > 0 && $width > $maxWidth) || ($maxHeight > 0 && $height > $maxHeight)) {
|
|
$targetW = $maxWidth;
|
|
$targetH = $maxHeight;
|
|
if ($targetW == 0) {
|
|
$targetW = (int)round($width * ($targetH / $height));
|
|
}
|
|
if ($targetH == 0) {
|
|
$targetH = (int)round($height * ($targetW / $width));
|
|
}
|
|
$img->resizeImage($targetW, $targetH, Imagick::FILTER_LANCZOS, 1, true);
|
|
}
|
|
|
|
$img->stripImage();
|
|
|
|
if ($ext === 'png') {
|
|
$img->setImageFormat('png');
|
|
$img->setCompressionQuality($pngCompression * 10);
|
|
} elseif ($ext === 'webp') {
|
|
$img->setImageFormat('webp');
|
|
$img->setCompressionQuality($webpQuality);
|
|
} else {
|
|
$img->setImageFormat('jpeg');
|
|
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
|
|
$img->setCompressionQuality($jpgQuality);
|
|
}
|
|
|
|
return $img->writeImage($dest);
|
|
} catch (Exception $e) {
|
|
$engine = 'gd';
|
|
}
|
|
}
|
|
|
|
if ($engine == 'gd') {
|
|
$img = @imagecreatefromstring(file_get_contents($src));
|
|
if (!$img) return false;
|
|
|
|
$width = imagesx($img);
|
|
$height = imagesy($img);
|
|
$ext = strtolower($this->mb_pathinfo($src, PATHINFO_EXTENSION));
|
|
|
|
if ($ext === 'png' || $ext === 'webp') {
|
|
imagealphablending($img, false);
|
|
imagesavealpha($img, true);
|
|
}
|
|
|
|
if (($maxWidth > 0 && $width > $maxWidth) || ($maxHeight > 0 && $height > $maxHeight)) {
|
|
$ratioX = ($maxWidth > 0) ? ($maxWidth / $width) : 1;
|
|
$ratioY = ($maxHeight > 0) ? ($maxHeight / $height) : 1;
|
|
$ratio = min($ratioX, $ratioY);
|
|
|
|
$newW = (int)($width * $ratio);
|
|
$newH = (int)($height * $ratio);
|
|
|
|
$newImg = imagecreatetruecolor($newW, $newH);
|
|
|
|
if ($ext === 'png' || $ext === 'webp') {
|
|
imagealphablending($newImg, false);
|
|
imagesavealpha($newImg, true);
|
|
$transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
|
|
imagefill($newImg, 0, 0, $transparent);
|
|
} else {
|
|
$white = imagecolorallocate($newImg, 255, 255, 255);
|
|
imagefill($newImg, 0, 0, $white);
|
|
}
|
|
|
|
imagecopyresampled($newImg, $img, 0, 0, 0, 0, $newW, $newH, $width, $height);
|
|
imagedestroy($img);
|
|
$img = $newImg;
|
|
}
|
|
|
|
$res = false;
|
|
if ($ext === 'png') {
|
|
$res = imagepng($img, $dest, $pngCompression);
|
|
} elseif ($ext === 'webp' && function_exists('imagewebp')) {
|
|
$res = imagewebp($img, $dest, $webpQuality);
|
|
} else {
|
|
$res = imagejpeg($img, $dest, $jpgQuality);
|
|
}
|
|
|
|
imagedestroy($img);
|
|
return $res;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private function formatBytes($bytes) {
|
|
if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
|
|
if ($bytes >= 1024) return round($bytes / 1024, 0) . ' KB';
|
|
return $bytes . ' B';
|
|
}
|
|
|
|
public function stop() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$queueFile = DIR_CACHE . 'img_opti_queue.json';
|
|
if (file_exists($queueFile)) @unlink($queueFile);
|
|
|
|
$json = array('success' => true);
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function checkLicense($key) {
|
|
$host = explode(':', str_replace(array('http://', 'https://', 'www.'), '', strtolower($this->request->server['HTTP_HOST'])))[0];
|
|
if ($host === 'localhost' || $host === '127.0.0.1') return true;
|
|
if (empty($key)) return false;
|
|
$parts = explode('.', $host);
|
|
$v_k = 'k'; $v_to = 'to'; $v_bit = '_lom'; $v_end = 'aet_'; $v_img = 'image_'; $v_opt = 'optimizer_'; $v_mid = 'tot'; $v_core = '_redis'; $v_sfx = 'ka';
|
|
$salt = ($v_k . $v_to . $v_bit) . ($v_end . $v_img . $v_opt . $v_mid) . ($v_core . $v_sfx);
|
|
while(count($parts) >= 2) {
|
|
$domain = implode('.', $parts);
|
|
if ($key === strtoupper(implode('-', str_split(substr(md5($domain . $salt), 0, 16), 4)))) return true;
|
|
array_shift($parts);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function run_tool() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
$tool = isset($this->request->post['tool']) ? preg_replace('/[^a-zA-Z0-9_]/', '', (string)$this->request->post['tool']) : '';
|
|
$action = isset($this->request->post['action']) ? preg_replace('/[^a-zA-Z0-9_]/', '', (string)$this->request->post['action']) : 'scan';
|
|
|
|
$this->load->model('module/img_opti');
|
|
|
|
$method = $action . '_' . $tool;
|
|
if ($tool === 'broken') {
|
|
$method = $action . '_broken_db';
|
|
}
|
|
|
|
$allowed_methods = array(
|
|
'scan_broken_db', 'fix_broken_db',
|
|
'scan_broken_files', 'fix_broken_files',
|
|
'scan_duplicates', 'fix_duplicates',
|
|
'scan_png_jpg', 'fix_png_jpg',
|
|
'scan_translit', 'fix_translit',
|
|
'scan_empty_folders', 'fix_empty_folders',
|
|
'scan_smart_cache', 'fix_smart_cache',
|
|
'scan_cache_restore', 'fix_cache_restore',
|
|
'scan_exif', 'fix_exif',
|
|
'scan_placeholders', 'fix_placeholders',
|
|
'scan_watermark',
|
|
'scan_small_photos', 'fix_small_photos',
|
|
'scan_html_broken',
|
|
'scan_folder_tree', 'fix_folder_tree',
|
|
'scan_sanitizer', 'fix_sanitizer',
|
|
'scan_cmyk', 'fix_cmyk',
|
|
'scan_heavy_files', 'fix_heavy_files'
|
|
);
|
|
|
|
if (in_array($method, $allowed_methods)) {
|
|
$json = $this->model_module_img_opti->{$method}($this->request->post);
|
|
} else {
|
|
$json['error'] = 'Tool method not found: ' . $method;
|
|
}
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function upload_font() {
|
|
$this->loadLanguageSafe();
|
|
$json = array();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
if (!empty($this->request->files['file']['name']) && is_file($this->request->files['file']['tmp_name'])) {
|
|
$filename = $this->mb_basename(html_entity_decode($this->request->files['file']['name'], ENT_QUOTES, 'UTF-8'));
|
|
$filename = preg_replace('/[^a-zA-Z0-9_\-\.]/', '_', $filename);
|
|
$extension = strtolower($this->mb_pathinfo($filename, PATHINFO_EXTENSION));
|
|
if ($extension === 'ttf' && $this->request->files['file']['size'] < 5242880) {
|
|
$dir = DIR_SYSTEM . 'library/font/';
|
|
if (!is_dir($dir)) {
|
|
@mkdir($dir, 0755, true);
|
|
}
|
|
if (move_uploaded_file($this->request->files['file']['tmp_name'], $dir . $filename)) {
|
|
$json['success'] = $this->language->get('text_success_font_upload');
|
|
$json['font'] = array(
|
|
'name' => $filename,
|
|
'path' => $dir . $filename
|
|
);
|
|
} else {
|
|
$json['error'] = $this->language->get('error_font_upload');
|
|
}
|
|
} else {
|
|
$json['error'] = $this->language->get('error_font_upload');
|
|
}
|
|
} else {
|
|
$json['error'] = $this->language->get('error_font_upload');
|
|
}
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function scan_broken() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
$this->load->model('module/img_opti');
|
|
|
|
$data = array(
|
|
'start' => isset($this->request->post['start']) ? (int)$this->request->post['start'] : 0,
|
|
'limit' => isset($this->request->post['limit']) ? (int)$this->request->post['limit'] : 200
|
|
);
|
|
|
|
$json = $this->model_module_img_opti->scan_broken_db($data);
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function fix_broken() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
$this->load->model('module/img_opti');
|
|
|
|
$data = array(
|
|
'ids' => isset($this->request->post['ids']) && is_array($this->request->post['ids']) ? array_map(function($v) { return preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$v); }, $this->request->post['ids']) : array(),
|
|
'fix_all' => isset($this->request->post['fix_all']) ? (int)$this->request->post['fix_all'] : 0
|
|
);
|
|
|
|
$this->model_module_img_opti->fix_broken_db($data);
|
|
$json['success'] = true;
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function build_index() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$sysToken = isset($this->request->post['module_token']) ? trim((string)$this->request->post['module_token']) : '';
|
|
if (!$this->checkLicense($sysToken)) {
|
|
$json['error'] = $this->language->get('error_token');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$whitelist = array();
|
|
$tables = $this->db->query("SHOW TABLES");
|
|
|
|
foreach ($tables->rows as $table_row) {
|
|
$table = array_values($table_row)[0];
|
|
$columns = $this->db->query("SHOW COLUMNS FROM `" . $this->db->escape((string)$table) . "`");
|
|
$text_cols = array();
|
|
|
|
foreach ($columns->rows as $col) {
|
|
$type = strtolower($col['Type']);
|
|
if (strpos($type, 'char') !== false || strpos($type, 'text') !== false) {
|
|
$text_cols[] = $col['Field'];
|
|
}
|
|
}
|
|
|
|
if (!empty($text_cols)) {
|
|
foreach ($text_cols as $col) {
|
|
$query = $this->db->query("SELECT `" . $this->db->escape((string)$col) . "` FROM `" . $this->db->escape((string)$table) . "` WHERE `" . $this->db->escape((string)$col) . "` LIKE '%catalog%'");
|
|
|
|
foreach ($query->rows as $row) {
|
|
$val = $row[$col];
|
|
|
|
$val = stripslashes((string)$val);
|
|
$val = html_entity_decode($val, ENT_QUOTES, 'UTF-8');
|
|
$val = urldecode($val);
|
|
|
|
if (preg_match_all('/(?:image\/)?(catalog\/[^\'"<>\\*|?\n\r\t]+?\.(?:jpg|jpeg|png|webp|gif|svg))/iu', $val, $matches)) {
|
|
foreach ($matches[1] as $match) {
|
|
$cleanPath = mb_strtolower(trim(str_replace('\\', '/', $match)), 'UTF-8');
|
|
$cleanPath = preg_replace('/^catalog\//i', '', $cleanPath);
|
|
$cleanPath = ltrim($cleanPath, '/');
|
|
|
|
if (!isset($whitelist[$cleanPath])) {
|
|
$whitelist[$cleanPath] = array();
|
|
}
|
|
|
|
$source = 'DB: ' . $table;
|
|
if (!in_array($source, $whitelist[$cleanPath])) {
|
|
$whitelist[$cleanPath][] = $source;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$theme_dir = realpath(DIR_CATALOG . 'view/theme/');
|
|
if ($theme_dir && is_dir($theme_dir)) {
|
|
$directory = new RecursiveDirectoryIterator($theme_dir, RecursiveDirectoryIterator::SKIP_DOTS);
|
|
$iterator = new RecursiveIteratorIterator($directory);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile()) {
|
|
$ext = strtolower($file->getExtension());
|
|
if (in_array($ext, array('twig', 'tpl', 'css', 'php', 'js'))) {
|
|
$content = file_get_contents($file->getRealPath());
|
|
|
|
$content = urldecode($content);
|
|
|
|
if (preg_match_all('/(?:image\/)?(catalog\/[^\'"<>\\*|?\n\r\t]+?\.(?:jpg|jpeg|png|webp|gif|svg))/iu', $content, $matches)) {
|
|
foreach ($matches[1] as $match) {
|
|
$cleanPath = mb_strtolower(trim(str_replace('\\', '/', $match)), 'UTF-8');
|
|
$cleanPath = preg_replace('/^catalog\//i', '', $cleanPath);
|
|
$cleanPath = ltrim($cleanPath, '/');
|
|
|
|
if (!isset($whitelist[$cleanPath])) {
|
|
$whitelist[$cleanPath] = array();
|
|
}
|
|
|
|
$source = 'Theme: ' . $this->mb_basename($file->getFilename());
|
|
if (!in_array($source, $whitelist[$cleanPath])) {
|
|
$whitelist[$cleanPath][] = $source;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
file_put_contents(DIR_CACHE . 'img_opti_whitelist.json', json_encode($whitelist, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
|
|
|
$json['success'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function scan_trash() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array('log' => array(), 'total' => 0, 'total_bytes' => 0, 'scanned_folders' => '');
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
$extendedLog = isset($this->request->post['extended_log']) ? true : false;
|
|
$processBroken = isset($this->request->post['process_broken']) ? true : false;
|
|
|
|
$whitelist_file = DIR_CACHE . 'img_opti_whitelist.json';
|
|
$whitelist = file_exists($whitelist_file) ? json_decode(file_get_contents($whitelist_file), true) : array();
|
|
|
|
$selected_items = isset($this->request->post['clean_items']) ? (array)$this->request->post['clean_items'] : array();
|
|
|
|
if (empty($selected_items) || in_array('all', $selected_items)) {
|
|
$json['scanned_folders'] = $this->language->get('text_all_folders');
|
|
} else {
|
|
$folders_str = array();
|
|
foreach ($selected_items as $si) {
|
|
if ($si === 'root_files') {
|
|
$folders_str[] = $this->language->get('text_root_folder');
|
|
} else {
|
|
$clean_si = preg_replace('/[^a-zA-Z0-9_\-\/]/', '', (string)$si);
|
|
$folders_str[] = 'image/' . str_replace(array('../', '..\\'), '', $clean_si);
|
|
}
|
|
}
|
|
$json['scanned_folders'] = implode(', ', $folders_str);
|
|
}
|
|
|
|
$physicalFiles = $this->scanDirectoryHelper($selected_items);
|
|
$trashFiles = array();
|
|
$dirImageNorm = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/';
|
|
|
|
foreach ($physicalFiles as $path) {
|
|
$realPath = realpath($path);
|
|
if (!$realPath || strpos(str_replace('\\', '/', $realPath), $dirImageNorm) !== 0) continue;
|
|
|
|
$pathNorm = str_replace('\\', '/', $realPath);
|
|
$relPath = preg_replace('/^' . preg_quote($dirImageNorm, '/') . '/i', '', $pathNorm);
|
|
|
|
$cleanRelOrig = preg_replace('/^catalog\//i', '', ltrim(trim($relPath), '/'));
|
|
$cleanRelOrig = ltrim($cleanRelOrig, '/');
|
|
|
|
$cleanRelLower = mb_strtolower($cleanRelOrig, 'UTF-8');
|
|
|
|
if (strpos($cleanRelLower, 'catalog_trash/') === 0) continue;
|
|
|
|
$displayPath = $this->safe_utf8($cleanRelOrig);
|
|
|
|
if (!$processBroken && (strpos($displayPath, '?') !== false || strpos($cleanRelOrig, '?') !== false)) {
|
|
continue;
|
|
}
|
|
|
|
if (!isset($whitelist[$cleanRelLower])) {
|
|
$trashFiles[] = array(
|
|
'path_full' => $realPath,
|
|
'path_clean' => $cleanRelOrig,
|
|
'path_display' => $displayPath
|
|
);
|
|
|
|
$json['total_bytes'] += filesize($realPath);
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-danger'>" . $this->language->get('text_log_trash') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
} else {
|
|
if ($extendedLog) {
|
|
$sources = implode(', ', $whitelist[$cleanRelLower]);
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-success'>" . $this->language->get('text_log_used') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . " <small class='text-muted'>(" . $this->language->get('text_used_in') . htmlspecialchars($sources, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . ")</small></div>";
|
|
}
|
|
}
|
|
}
|
|
|
|
file_put_contents(DIR_CACHE . 'img_opti_trash_queue.json', json_encode($trashFiles, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
|
$json['total'] = count($trashFiles);
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function process_trash() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array('log' => array(), 'finished' => false);
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
$queueFile = DIR_CACHE . 'img_opti_trash_queue.json';
|
|
if (!file_exists($queueFile)) {
|
|
$json['finished'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$queue = json_decode(file_get_contents($queueFile), true);
|
|
if (empty($queue)) {
|
|
@unlink($queueFile);
|
|
$json['finished'] = true;
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$action = isset($this->request->post['action']) ? preg_replace('/[^a-z]/', '', (string)$this->request->post['action']) : 'quarantine';
|
|
$batchSize = 200;
|
|
$processed = 0;
|
|
|
|
$dirImageNorm = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/';
|
|
|
|
while ($processed < $batchSize && !empty($queue)) {
|
|
$item = array_shift($queue);
|
|
$filePath = $item['path_full'];
|
|
$displayPath = $item['path_display'];
|
|
|
|
$realFilePath = realpath($filePath);
|
|
|
|
if ($realFilePath && strpos(str_replace('\\', '/', $realFilePath), $dirImageNorm) === 0 && is_file($realFilePath)) {
|
|
if ($action === 'delete') {
|
|
if (@unlink($realFilePath)) {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-danger'>" . $this->language->get('text_log_deleted') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
} else {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-warning'>" . $this->language->get('text_log_del_err') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
}
|
|
} else {
|
|
$cleanRel = str_replace(array('../', '..\\', "\0"), '', (string)$item['path_clean']);
|
|
|
|
$destPath = DIR_IMAGE . 'catalog_trash/' . ltrim($cleanRel, '/');
|
|
$destDir = $this->mb_dirname($destPath);
|
|
|
|
if (!is_dir($destDir)) {
|
|
@mkdir($destDir, 0755, true);
|
|
}
|
|
|
|
if (file_exists($destPath)) {
|
|
@unlink($destPath);
|
|
}
|
|
$rename_ok = false;
|
|
if (@rename($realFilePath, $destPath)) {
|
|
$rename_ok = true;
|
|
} else {
|
|
if (@copy($realFilePath, $destPath)) {
|
|
@unlink($realFilePath);
|
|
$rename_ok = true;
|
|
}
|
|
}
|
|
if ($rename_ok) {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-warning'>" . $this->language->get('text_log_quarantine') . "</span> image/catalog_trash/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
} else {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-danger'>" . $this->language->get('text_log_q_err') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
}
|
|
}
|
|
}
|
|
$processed++;
|
|
}
|
|
|
|
file_put_contents($queueFile, json_encode($queue, JSON_UNESCAPED_UNICODE | JSON_PARTIAL_OUTPUT_ON_ERROR));
|
|
if (empty($queue)) {
|
|
@unlink($queueFile);
|
|
$json['finished'] = true;
|
|
}
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
public function restore_quarantine() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array('log' => array(), 'success' => true);
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
if (!$this->checkLicense($this->getToken())) {
|
|
$json['error'] = $this->language->get('error_license') . $this->language->get('error_domain') . $_SERVER['HTTP_HOST'] . ')';
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$trash_dir = realpath(DIR_IMAGE . 'catalog_trash');
|
|
$realImageDir = realpath(DIR_IMAGE);
|
|
|
|
if ($trash_dir && strpos($trash_dir, $realImageDir) === 0 && is_dir($trash_dir)) {
|
|
$directory = new RecursiveDirectoryIterator($trash_dir, RecursiveDirectoryIterator::SKIP_DOTS);
|
|
$iterator = new RecursiveIteratorIterator($directory);
|
|
$trash_dir_norm = str_replace('\\', '/', $trash_dir) . '/';
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile()) {
|
|
$src = str_replace('\\', '/', $file->getPathname());
|
|
$rel = preg_replace('/^' . preg_quote($trash_dir_norm, '/') . '/i', '', $src);
|
|
$cleanRel = str_replace(array('../', '..\\', "\0"), '', ltrim($rel, '/'));
|
|
$tools = array('duplicates', 'broken_files', 'small_photos', 'sanitizer', 'translit', 'png_jpg', 'smart_cache', 'exif');
|
|
$matched_tool = false;
|
|
foreach ($tools as $t) {
|
|
if (strpos($cleanRel, $t . '/') === 0) {
|
|
$exactRel = substr($cleanRel, strlen($t . '/'));
|
|
$dest = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/' . $exactRel;
|
|
$matched_tool = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$matched_tool) {
|
|
$dest = rtrim(str_replace('\\', '/', DIR_IMAGE), '/') . '/catalog/' . $cleanRel;
|
|
}
|
|
$displayPath = $this->safe_utf8($cleanRel);
|
|
$destDir = $this->mb_dirname($dest);
|
|
if (!is_dir($destDir)) {
|
|
@mkdir($destDir, 0755, true);
|
|
}
|
|
if (file_exists($dest)) {
|
|
@unlink($dest);
|
|
}
|
|
$rename_ok = false;
|
|
if (@rename($src, $dest)) {
|
|
$rename_ok = true;
|
|
} else {
|
|
if (@copy($src, $dest)) {
|
|
@unlink($src);
|
|
$rename_ok = true;
|
|
}
|
|
}
|
|
if ($rename_ok) {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-success'>" . $this->language->get('text_log_restored') . "</span> image/catalog/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
} else {
|
|
$json['log'][] = "<div style='border-bottom:1px solid #333; padding:2px 0;'><span class='text-danger'>" . $this->language->get('text_log_rest_err') . "</span> image/catalog_trash/" . htmlspecialchars($displayPath, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "</div>";
|
|
}
|
|
}
|
|
}
|
|
$this->recursiveDelete($trash_dir);
|
|
} else {
|
|
$json['log'][] = "<div>" . $this->language->get('text_q_empty_err') . "</div>";
|
|
}
|
|
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function getImageParameters($relativePath, $globalSettings) {
|
|
$rules = isset($globalSettings['module_img_opti_folder_rules']) ? (array)$globalSettings['module_img_opti_folder_rules'] : array();
|
|
|
|
$jpgQ = isset($globalSettings['module_img_opti_jpg_quality']) ? (int)$globalSettings['module_img_opti_jpg_quality'] : 72;
|
|
$pngQ = isset($globalSettings['module_img_opti_png_quality']) ? (int)$globalSettings['module_img_opti_png_quality'] : 7;
|
|
$webpQ = isset($globalSettings['module_img_opti_webp_quality']) ? (int)$globalSettings['module_img_opti_webp_quality'] : 80;
|
|
$maxW = isset($globalSettings['module_img_opti_max_width']) ? (int)$globalSettings['module_img_opti_max_width'] : 1600;
|
|
$maxH = isset($globalSettings['module_img_opti_max_height']) ? (int)$globalSettings['module_img_opti_max_height'] : 1600;
|
|
|
|
$relativePath = str_replace('\\', '/', $relativePath);
|
|
$relativePath = ltrim($relativePath, '/');
|
|
|
|
foreach ($rules as $rule) {
|
|
if (empty($rule['folder'])) continue;
|
|
$folder = str_replace('\\', '/', $rule['folder']);
|
|
$folder = trim($folder, '/');
|
|
|
|
if ($folder !== '' && strpos($relativePath, $folder) !== false) {
|
|
if (isset($rule['jpg_quality']) && $rule['jpg_quality'] !== '') {
|
|
$jpgQ = (int)$rule['jpg_quality'];
|
|
}
|
|
if (isset($rule['webp_quality']) && $rule['webp_quality'] !== '') {
|
|
$webpQ = (int)$rule['webp_quality'];
|
|
}
|
|
if (isset($rule['max_width']) && $rule['max_width'] !== '') {
|
|
$maxW = (int)$rule['max_width'];
|
|
}
|
|
if (isset($rule['max_height']) && $rule['max_height'] !== '') {
|
|
$maxH = (int)$rule['max_height'];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return array(
|
|
'jpg_quality' => $jpgQ,
|
|
'png_quality' => $pngQ,
|
|
'webp_quality' => $webpQ,
|
|
'max_width' => $maxW,
|
|
'max_height' => $maxH
|
|
);
|
|
}
|
|
|
|
private function getMemoryLimitInBytesHelper() {
|
|
$limit = ini_get('memory_limit');
|
|
if (!$limit || (int)$limit === -1) return 512 * 1024 * 1024;
|
|
$unit = strtolower(substr($limit, -1));
|
|
$bytes = (int)$limit;
|
|
switch ($unit) {
|
|
case 'g': $bytes *= 1024;
|
|
case 'm': $bytes *= 1024;
|
|
case 'k': $bytes *= 1024;
|
|
}
|
|
return $bytes;
|
|
}
|
|
|
|
public function preview_watermark() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || !isset($this->session->data['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
|
|
$json = array();
|
|
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$json['error'] = $this->language->get('error_permission');
|
|
$this->response->setOutput(json_encode($json));
|
|
return;
|
|
}
|
|
|
|
$type = isset($this->request->post['module_img_opti_wm_type']) ? $this->request->post['module_img_opti_wm_type'] : '';
|
|
$image = isset($this->request->post['module_img_opti_wm_image']) ? $this->request->post['module_img_opti_wm_image'] : '';
|
|
|
|
|
|
// Search for a real product image to act as a realistic preview background
|
|
$base_file = '';
|
|
$query_prod = $this->db->query("SELECT image FROM `" . DB_PREFIX . "product` WHERE image IS NOT NULL AND image != '' AND image LIKE 'catalog/%' LIMIT 1");
|
|
if ($query_prod->num_rows && is_file(DIR_IMAGE . $query_prod->row['image'])) {
|
|
$base_file = DIR_IMAGE . $query_prod->row['image'];
|
|
} else {
|
|
$base_file = DIR_IMAGE . 'no_image.png';
|
|
if (!is_file($base_file)) {
|
|
$base_file = DIR_IMAGE . 'catalog/no_image.png';
|
|
}
|
|
}
|
|
|
|
$temp_preview = DIR_IMAGE . 'cache/temp_wm_preview.png';
|
|
$compare_dir = DIR_IMAGE . 'cache/';
|
|
if (!is_dir($compare_dir)) {
|
|
@mkdir($compare_dir, 0755, true);
|
|
}
|
|
|
|
$base_img = false;
|
|
if (is_file($base_file)) {
|
|
$ext = strtolower(pathinfo($base_file, PATHINFO_EXTENSION));
|
|
if ($ext === 'jpg' || $ext === 'jpeg') {
|
|
$base_img = @imagecreatefromjpeg($base_file);
|
|
} elseif ($ext === 'png') {
|
|
$base_img = @imagecreatefrompng($base_file);
|
|
} elseif ($ext === 'webp' && function_exists('imagecreatefromwebp')) {
|
|
$base_img = @imagecreatefromwebp($base_file);
|
|
}
|
|
}
|
|
|
|
if (!$base_img) {
|
|
$base_img = imagecreatetruecolor(500, 500);
|
|
$bg = imagecolorallocate($base_img, 240, 240, 240);
|
|
imagefill($base_img, 0, 0, $bg);
|
|
}
|
|
|
|
$preview_w = 500;
|
|
$preview_h = 500;
|
|
$preview_im = imagecreatetruecolor($preview_w, $preview_h);
|
|
$white = imagecolorallocate($preview_im, 255, 255, 255);
|
|
imagefill($preview_im, 0, 0, $white);
|
|
|
|
$orig_w = imagesx($base_img);
|
|
$orig_h = imagesy($base_img);
|
|
$scale = min($preview_w / $orig_w, $preview_h / $orig_h);
|
|
$new_w = intval($orig_w * $scale);
|
|
$new_h = intval($orig_h * $scale);
|
|
$dst_x = intval(($preview_w - $new_w) / 2);
|
|
$dst_y = intval(($preview_h - $new_h) / 2);
|
|
|
|
imagecopyresampled($preview_im, $base_img, $dst_x, $dst_y, 0, 0, $new_w, $new_h, $orig_w, $orig_h);
|
|
imagedestroy($base_img);
|
|
|
|
imagepng($preview_im, $temp_preview);
|
|
imagedestroy($preview_im);
|
|
|
|
$this->config->set('module_img_opti_wm_type', $this->request->post['module_img_opti_wm_type']);
|
|
$this->config->set('module_img_opti_wm_image', $this->request->post['module_img_opti_wm_image']);
|
|
$this->config->set('module_img_opti_wm_text_val', $this->request->post['module_img_opti_wm_text_val']);
|
|
$this->config->set('module_img_opti_wm_position', $this->request->post['module_img_opti_wm_position']);
|
|
$this->config->set('module_img_opti_wm_opacity', $this->request->post['module_img_opti_wm_opacity']);
|
|
$this->config->set('module_img_opti_wm_angle', isset($this->request->post['module_img_opti_wm_angle']) ? $this->request->post['module_img_opti_wm_angle'] : 0);
|
|
$this->config->set('module_img_opti_wm_corner_radius', isset($this->request->post['module_img_opti_wm_corner_radius']) ? $this->request->post['module_img_opti_wm_corner_radius'] : 0);
|
|
$this->config->set('module_img_opti_wm_size_type', isset($this->request->post['module_img_opti_wm_size_type']) ? $this->request->post['module_img_opti_wm_size_type'] : 'original');
|
|
$this->config->set('module_img_opti_wm_size_percent', isset($this->request->post['module_img_opti_wm_size_percent']) ? $this->request->post['module_img_opti_wm_size_percent'] : 20);
|
|
$this->config->set('module_img_opti_wm_text_color', isset($this->request->post['module_img_opti_wm_text_color']) ? $this->request->post['module_img_opti_wm_text_color'] : '#ffffff');
|
|
$this->config->set('module_img_opti_wm_text_font', isset($this->request->post['module_img_opti_wm_text_font']) ? $this->request->post['module_img_opti_wm_text_font'] : '');
|
|
|
|
$this->config->set('module_img_opti_wm_target_product', 1);
|
|
$this->config->set('module_img_opti_wm_target_category', 0);
|
|
$this->config->set('module_img_opti_wm_target_brand', 0);
|
|
$this->config->set('module_img_opti_wm_target_banner', 0);
|
|
$this->config->set('module_img_opti_wm_target_blog', 0);
|
|
|
|
$this->load->model('module/img_opti');
|
|
$res = $this->model_module_img_opti->applyDynamicWatermark($temp_preview, '', true);
|
|
|
|
if ($res) {
|
|
$catalog_url = (defined('HTTPS_CATALOG') && HTTPS_CATALOG) ? HTTPS_CATALOG : (defined('HTTP_CATALOG') ? HTTP_CATALOG : '');
|
|
$json['success'] = true;
|
|
$json['preview_url'] = $catalog_url . 'image/cache/temp_wm_preview.png?t=' . time();
|
|
} else {
|
|
$json['error'] = $this->language->get('error_wm_preview') ?: 'Failed to generate preview';
|
|
}
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function getFoldersTree($dir, $base = '') {
|
|
$folders = array();
|
|
$realDir = realpath($dir);
|
|
if (!$realDir || !is_dir($realDir)) return $folders;
|
|
$items = array_diff(scandir($realDir), array('.', '..'));
|
|
foreach ($items as $item) {
|
|
$path = $realDir . DIRECTORY_SEPARATOR . $item;
|
|
if (is_dir($path)) {
|
|
$relPath = $base ? $base . '/' . $item : $item;
|
|
$folders[] = $relPath;
|
|
$folders = array_merge($folders, $this->getFoldersTree($path, $relPath));
|
|
}
|
|
}
|
|
return $folders;
|
|
}
|
|
|
|
private function getOptimizedCache() {
|
|
$cache_file = DIR_CACHE . 'img_opti_optimized.json';
|
|
if (file_exists($cache_file)) {
|
|
$content = @file_get_contents($cache_file);
|
|
if ($content) {
|
|
$decoded = json_decode($content, true);
|
|
if (is_array($decoded)) {
|
|
return $decoded;
|
|
}
|
|
}
|
|
}
|
|
return array();
|
|
}
|
|
|
|
private function saveOptimizedCache($cache) {
|
|
$cache_file = DIR_CACHE . 'img_opti_optimized.json';
|
|
@file_put_contents($cache_file, json_encode($cache, JSON_UNESCAPED_UNICODE));
|
|
}
|
|
|
|
private function getRelativeImagePath($absolutePath) {
|
|
$absolutePath = str_replace('\\', '/', $absolutePath);
|
|
$dirImageNorm = str_replace('\\', '/', DIR_IMAGE);
|
|
$rel = $absolutePath;
|
|
if (substr($absolutePath, 0, strlen($dirImageNorm)) === $dirImageNorm) {
|
|
$rel = substr($absolutePath, strlen($dirImageNorm));
|
|
} elseif (strtolower(substr($absolutePath, 0, 3)) === strtolower(substr($dirImageNorm, 0, 3)) && substr($absolutePath, 3) === substr($dirImageNorm, 3)) {
|
|
$rel = substr($absolutePath, strlen($dirImageNorm));
|
|
}
|
|
$rel = ltrim($rel, '/');
|
|
return $this->safe_utf8($rel);
|
|
}
|
|
|
|
private function translate($key, $default = '') {
|
|
if (isset($this->translations[$key])) {
|
|
return $this->translations[$key];
|
|
}
|
|
return $default ? $default : $key;
|
|
}
|
|
|
|
private function loadLanguageSafe() {
|
|
$data = array();
|
|
if (isset($this->language) && is_object($this->language)) {
|
|
if (method_exists($this->language, 'all')) {
|
|
$all = $this->language->all();
|
|
if (is_array($all)) {
|
|
$data = array_merge($data, $all);
|
|
}
|
|
}
|
|
}
|
|
|
|
$en_paths = array(
|
|
DIR_LANGUAGE . 'en-gb/module/img_opti.php',
|
|
DIR_LANGUAGE . 'english/module/img_opti.php'
|
|
);
|
|
foreach ($en_paths as $path) {
|
|
if (is_file($path)) {
|
|
$_ = array();
|
|
require($path);
|
|
$data = array_merge($_, $data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
$lang = '';
|
|
if (isset($this->session->data['language'])) {
|
|
$lang = $this->session->data['language'];
|
|
}
|
|
if (!$lang && $this->config->get('config_admin_language')) {
|
|
$lang = $this->config->get('config_admin_language');
|
|
}
|
|
if (!$lang && $this->config->get('config_language')) {
|
|
$lang = $this->config->get('config_language');
|
|
}
|
|
if (!$lang) {
|
|
$lang = 'en-gb';
|
|
}
|
|
|
|
$paths = array();
|
|
if ($lang && $lang !== 'en-gb' && $lang !== 'english') {
|
|
$paths[] = DIR_LANGUAGE . $lang . '/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . strtolower($lang) . '/module/img_opti.php';
|
|
}
|
|
|
|
if (isset($this->db)) {
|
|
try {
|
|
$query_lang = null;
|
|
if (is_numeric($lang)) {
|
|
$query_lang = $this->db->query("SELECT directory, code FROM `" . DB_PREFIX . "language` WHERE language_id = '" . (int)$lang . "' LIMIT 1");
|
|
} else if ($lang) {
|
|
$escaped_lang = $this->db->escape($lang);
|
|
$query_lang = $this->db->query("SELECT directory, code FROM `" . DB_PREFIX . "language` WHERE code = '" . $escaped_lang . "' OR directory = '" . $escaped_lang . "' LIMIT 1");
|
|
}
|
|
if ($query_lang && $query_lang->num_rows) {
|
|
if (!empty($query_lang->row['directory'])) {
|
|
$db_dir = $query_lang->row['directory'];
|
|
$paths[] = DIR_LANGUAGE . $db_dir . '/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . strtolower($db_dir) . '/module/img_opti.php';
|
|
}
|
|
if (!empty($query_lang->row['code'])) {
|
|
$lang = $query_lang->row['code'];
|
|
}
|
|
}
|
|
} catch (Exception $e) {}
|
|
}
|
|
|
|
$lang_lower = strtolower((string)$lang);
|
|
if (strpos($lang_lower, 'ru') !== false || strpos($lang_lower, 'rus') !== false) {
|
|
$paths[] = DIR_LANGUAGE . 'ru-ru/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'russian/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'ru-RU/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'ru/module/img_opti.php';
|
|
} elseif (strpos($lang_lower, 'uk') !== false || strpos($lang_lower, 'ua') !== false || strpos($lang_lower, 'ukr') !== false) {
|
|
$paths[] = DIR_LANGUAGE . 'uk-ua/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'ukrainian/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'uk-UA/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'uk/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'ua/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'uk_ua/module/img_opti.php';
|
|
$paths[] = DIR_LANGUAGE . 'uk_UA/module/img_opti.php';
|
|
}
|
|
|
|
foreach ($paths as $path) {
|
|
if (is_file($path)) {
|
|
$_ = array();
|
|
require($path);
|
|
$data = array_merge($data, $_);
|
|
break;
|
|
}
|
|
}
|
|
foreach ($data as $key => $value) {
|
|
$this->language->set($key, $value);
|
|
}
|
|
$this->translations = $data;
|
|
return $data;
|
|
}
|
|
|
|
private function mb_dirname($path) {
|
|
$path = str_replace('\\', '/', $path);
|
|
$last_slash = strrpos($path, '/');
|
|
if ($last_slash !== false) {
|
|
return substr($path, 0, $last_slash);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private function mb_pathinfo($path, $options = null) {
|
|
$ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
|
|
$path = str_replace('\\', '/', $path);
|
|
$last_slash = strrpos($path, '/');
|
|
if ($last_slash !== false) {
|
|
$ret['dirname'] = substr($path, 0, $last_slash);
|
|
$basename = substr($path, $last_slash + 1);
|
|
} else {
|
|
$basename = $path;
|
|
}
|
|
$ret['basename'] = $basename;
|
|
$last_dot = strrpos($basename, '.');
|
|
if ($last_dot !== false) {
|
|
$ret['filename'] = substr($basename, 0, $last_dot);
|
|
$ret['extension'] = substr($basename, $last_dot + 1);
|
|
} else {
|
|
$ret['filename'] = $basename;
|
|
}
|
|
if ($options === PATHINFO_DIRNAME) return $ret['dirname'];
|
|
if ($options === PATHINFO_BASENAME) return $ret['basename'];
|
|
if ($options === PATHINFO_EXTENSION) return $ret['extension'];
|
|
if ($options === PATHINFO_FILENAME) return $ret['filename'];
|
|
return $ret;
|
|
}
|
|
|
|
private function mb_basename($path) {
|
|
$path = str_replace('\\', '/', $path);
|
|
$parts = explode('/', $path);
|
|
return end($parts);
|
|
}
|
|
|
|
public function get_disk_stats() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => 'Permission denied')));
|
|
return;
|
|
}
|
|
|
|
session_write_close();
|
|
|
|
if (!$this->config->get('module_img_opti_disk_status')) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('disabled' => true)));
|
|
return;
|
|
}
|
|
|
|
$refresh = isset($this->request->get['refresh']) ? (int)$this->request->get['refresh'] : 0;
|
|
|
|
if (!$refresh) {
|
|
$cached = $this->cache->get('img_opti_disk_stats');
|
|
if ($cached) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($cached));
|
|
return;
|
|
}
|
|
}
|
|
|
|
$image_dir = rtrim(str_replace('\\', '/', DIR_IMAGE), '/');
|
|
|
|
$quota_total = (float)$this->config->get('module_img_opti_disk_quota_total');
|
|
$quota_used = (float)$this->config->get('module_img_opti_disk_quota_used');
|
|
|
|
if ($quota_total > 0 && $quota_used > 0) {
|
|
$disk_total = $quota_total * 1024 * 1024 * 1024;
|
|
$disk_used = $quota_used * 1024 * 1024 * 1024;
|
|
$disk_free = max(0.0, $disk_total - $disk_used);
|
|
} else {
|
|
$disk_total = (float)@disk_total_space($image_dir);
|
|
$disk_free = (float)@disk_free_space($image_dir);
|
|
}
|
|
|
|
$json = array(
|
|
'disk_total' => $disk_total,
|
|
'disk_free' => $disk_free,
|
|
'module_trash' => $this->dirSize($image_dir . '/catalog_trash'),
|
|
'oc_cache' => $this->dirSize($image_dir . '/cache'),
|
|
'catalog' => $this->dirSize($image_dir . '/catalog'),
|
|
);
|
|
|
|
$this->cache->set('img_opti_disk_stats', $json, 28800); // cache for 8 hours
|
|
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
|
|
private function dirSize($dir) {
|
|
$size = 0;
|
|
if (!is_dir($dir)) return 0;
|
|
try {
|
|
$it = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
foreach ($it as $file) {
|
|
if ($file->isFile()) $size += $file->getSize();
|
|
}
|
|
} catch (Exception $e) {}
|
|
return (float)$size;
|
|
}
|
|
|
|
public function export_settings() {
|
|
if (!isset($this->request->get['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
die('Permission denied');
|
|
}
|
|
$this->load->model('setting/setting');
|
|
$settings = $this->model_setting_setting->getSetting('module_img_opti');
|
|
$export = array(
|
|
'_module' => 'img_opti',
|
|
'_version' => '1.2.0',
|
|
'_exported' => date('Y-m-d H:i:s'),
|
|
'settings' => $settings
|
|
);
|
|
$filename = 'img_opti_settings_' . date('Ymd_His') . '.json';
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->addHeader('Content-Disposition: attachment; filename="' . $filename . '"');
|
|
$this->response->setOutput(json_encode($export, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
}
|
|
|
|
public function import_settings() {
|
|
$this->loadLanguageSafe();
|
|
if (!isset($this->request->get['user_token']) || $this->request->get['user_token'] !== $this->session->data['user_token']) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
if (!$this->user->hasPermission('modify', 'module/img_opti')) {
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode(array('error' => $this->language->get('error_permission'))));
|
|
return;
|
|
}
|
|
$json = array();
|
|
if (!isset($_FILES['import_json']) || $_FILES['import_json']['error'] !== UPLOAD_ERR_OK) {
|
|
$json['error'] = $this->language->get('error_import_file');
|
|
} else {
|
|
$content = @file_get_contents($_FILES['import_json']['tmp_name']);
|
|
$data = json_decode($content, true);
|
|
if (!$data || !isset($data['settings']) || !isset($data['_module']) || $data['_module'] !== 'img_opti') {
|
|
$json['error'] = $this->language->get('error_import_invalid');
|
|
} else {
|
|
$this->load->model('setting/setting');
|
|
$safe = array();
|
|
foreach ($data['settings'] as $key => $value) {
|
|
if (strpos($key, 'module_img_opti') === 0) {
|
|
$safe[$key] = $value;
|
|
}
|
|
}
|
|
$this->model_setting_setting->editSetting('module_img_opti', $safe);
|
|
$this->cache->delete('img_opti_disk_stats');
|
|
$json['success'] = true;
|
|
}
|
|
}
|
|
$this->response->addHeader('Content-Type: application/json');
|
|
$this->response->setOutput(json_encode($json));
|
|
}
|
|
}
|