709 lines
42 KiB
XML
709 lines
42 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<modification>
|
|
<name>Image Optimizer [NAT]</name>
|
|
<id>Image_Optimizer_nat</id>
|
|
<code>img_opti_nat_v1</code>
|
|
<version>1.2.0</version>
|
|
<author>NAT</author>
|
|
<link>mailto:info@nat.od.ua</link>
|
|
|
|
<file path="admin/controller/common/column_left.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[if ($this->user->hasPermission('access', 'catalog/product')) {]]></search>
|
|
<add position="before"><![CDATA[
|
|
$this->load->model('setting/setting');
|
|
if ($this->config->get('module_img_opti_status')) {
|
|
$nat_pos = $this->config->get('module_img_opti_menu_position');
|
|
if ($nat_pos == 'sidebar' || $nat_pos == 'both') {
|
|
if ($this->user->hasPermission('access', 'extension/module/img_opti')) {
|
|
$nat_item = array(
|
|
'name' => 'Image Optimizer',
|
|
'href' => $this->url->link('extension/module/img_opti', 'user_token=' . $this->session->data['user_token'], true),
|
|
'children' => array()
|
|
);
|
|
|
|
$nat_menu_index = -1;
|
|
if (isset($data['menus'])) {
|
|
foreach ($data['menus'] as $k => $menu) {
|
|
if (isset($menu['id']) && $menu['id'] == 'menu-nat') {
|
|
$nat_menu_index = $k;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$exists = false;
|
|
if ($nat_menu_index > -1 && isset($data['menus'][$nat_menu_index]['children'])) {
|
|
foreach ($data['menus'][$nat_menu_index]['children'] as $child) {
|
|
if (isset($child['href']) && strpos($child['href'], 'route=extension/module/img_opti') !== false) {
|
|
$exists = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$exists) {
|
|
if ($nat_menu_index > -1) {
|
|
$data['menus'][$nat_menu_index]['children'][] = $nat_item;
|
|
} else {
|
|
$data['menus'][] = array(
|
|
'id' => 'menu-nat',
|
|
'icon' => 'fa-rocket fw',
|
|
'name' => '[NAT] Modules',
|
|
'href' => '',
|
|
'children' => array($nat_item)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="admin/controller/common/header.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[$data['logout'] = $this->url->link('common/logout']]></search>
|
|
<add position="before"><![CDATA[
|
|
if (!isset($data['nat_menus'])) {
|
|
$data['nat_menus'] = array();
|
|
}
|
|
$this->load->model('setting/setting');
|
|
if ($this->config->get('module_img_opti_status')) {
|
|
$nat_pos = $this->config->get('module_img_opti_menu_position');
|
|
if ($nat_pos == 'header' || $nat_pos == 'both') {
|
|
if ($this->user->hasPermission('access', 'extension/module/img_opti')) {
|
|
$exists = false;
|
|
foreach ($data['nat_menus'] as $m) {
|
|
if (isset($m['href']) && strpos($m['href'], 'route=extension/module/img_opti') !== false) {
|
|
$exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$exists) {
|
|
$data['nat_menus'][] = array(
|
|
'name' => 'Image Optimizer',
|
|
'href' => $this->url->link('extension/module/img_opti', 'user_token=' . $this->session->data['user_token'], true)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="admin/view/template/common/header.twig">
|
|
<operation error="skip">
|
|
<search><![CDATA[</head>]]></search>
|
|
<add position="before"><![CDATA[
|
|
<style>
|
|
a.img-thumbnail img, a.thumbnail img {
|
|
width: 100px !important;
|
|
height: 100px !important;
|
|
object-fit: contain !important;
|
|
}
|
|
</style>
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[<li><a href="{{ logout }}"]]></search>
|
|
<add position="before"><![CDATA[
|
|
{% if nat_menus is defined and nat_menus and not nat_menu_rendered|default(false) %}
|
|
{% set nat_menu_rendered = true %}
|
|
<li class="dropdown" id="nat-ecosystem-menu">
|
|
<a class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-rocket fa-lg" style="color:#e74c3c;"></i> <span class="hidden-xs hidden-sm hidden-md">[NAT]</span> <span class="caret"></span></a>
|
|
<ul class="dropdown-menu dropdown-menu-right" id="nat-ecosystem-list">
|
|
<li class="dropdown-header">Ecosystem [NAT]</li>
|
|
{% for n_menu in nat_menus %}
|
|
<li><a href="{{ n_menu.href }}"><i class="fa fa-angle-right"></i> {{ n_menu.name }}</a></li>
|
|
{% endfor %}
|
|
</ul>
|
|
</li>
|
|
{% endif %}
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="admin/controller/common/filemanager.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array(utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1)), $allowed)) {]]></search>
|
|
<add position="before"><![CDATA[
|
|
if ($this->config->get('module_img_opti_support_svg') && !in_array('svg', $allowed)) { $allowed[] = 'svg'; }
|
|
if ($this->config->get('module_img_opti_support_webp') && !in_array('webp', $allowed)) { $allowed[] = 'webp'; }
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array($file['type'], $allowed)) {]]></search>
|
|
<add position="before"><![CDATA[
|
|
if ($this->config->get('module_img_opti_support_svg') && !in_array('image/svg+xml', $allowed)) { $allowed[] = 'image/svg+xml'; }
|
|
if ($this->config->get('module_img_opti_support_webp')) {
|
|
if (!in_array('image/webp', $allowed)) { $allowed[] = 'image/webp'; }
|
|
if (!in_array('image/vnd.webp', $allowed)) { $allowed[] = 'image/vnd.webp'; }
|
|
if (!in_array('image/x-webp', $allowed)) { $allowed[] = 'image/x-webp'; }
|
|
$uploaded_ext = utf8_strtolower(utf8_substr(strrchr($filename, '.'), 1));
|
|
if ($uploaded_ext === 'webp' && !in_array($file['type'], $allowed)) {
|
|
$allowed[] = $file['type'];
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[move_uploaded_file($file['tmp_name'], $directory . '/' . $filename);]]></search>
|
|
<add position="before"><![CDATA[
|
|
if (strtolower(substr(strrchr($filename, '.'), 1)) === 'svg') {
|
|
$svg_content = @file_get_contents($file['tmp_name']);
|
|
if ($svg_content) {
|
|
$svg_content = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $svg_content);
|
|
$svg_content = preg_replace('/on\w+\s*=\s*["\'][^"\']*["\']/i', '', $svg_content);
|
|
$svg_content = preg_replace('/href\s*=\s*["\']\s*javascript:[^"\']*["\']/i', '', $svg_content);
|
|
@file_put_contents($file['tmp_name'], $svg_content);
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[$files = glob($directory . '/' . $filter_name . '*.{jpg,jpeg,png,gif,JPG,JPEG,PNG,GIF}', GLOB_BRACE);]]></search>
|
|
<add position="replace"><![CDATA[
|
|
$ext_list = array('jpg','jpeg','png','gif','JPG','JPEG','PNG','GIF');
|
|
if ($this->config->get('module_img_opti_support_svg')) {
|
|
$ext_list[] = 'svg'; $ext_list[] = 'SVG';
|
|
}
|
|
if ($this->config->get('module_img_opti_support_webp')) {
|
|
$ext_list[] = 'webp'; $ext_list[] = 'WEBP';
|
|
}
|
|
$files = glob($directory . '/' . $filter_name . '*.{' . implode(',', $ext_list) . '}', GLOB_BRACE);
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="catalog/model/tool/image.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[public function resize($filename, $width, $height) {]]></search>
|
|
<add position="after"><![CDATA[
|
|
$GLOBALS['registry'] = $this->registry;
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_auto_fake_jpg_guard') && !empty($filename)) {
|
|
$this->load->model('extension/module/img_opti');
|
|
try {
|
|
$filename = $this->model_extension_module_img_opti->checkAndFixFakeJpgOnTheFly($filename);
|
|
} catch (\Exception $e) {}
|
|
}
|
|
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) {
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback')) {
|
|
$fallback_image = $this->config->get('module_img_opti_fallback_image');
|
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
|
$filename = $fallback_image;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($filename) && strpos($filename, '.') !== false) {
|
|
$nat_ext = strtolower(substr(strrchr($filename, '.'), 1));
|
|
if ($nat_ext === 'svg' || ($nat_ext === 'webp' && !function_exists('imagewebp'))) {
|
|
if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on' || ($this->request->server['HTTPS'] == '1')))) {
|
|
return $this->config->get('config_ssl') . 'image/' . str_replace('\\', '/', $filename);
|
|
} else {
|
|
return $this->config->get('config_url') . 'image/' . str_replace('\\', '/', $filename);
|
|
}
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif')))]]></search>
|
|
<add position="replace"><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'webp')))]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF)))]]></search>
|
|
<add position="replace"><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF, defined('IMAGETYPE_WEBP') ? IMAGETYPE_WEBP : 18)))]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[$image->save(DIR_IMAGE . $image_new);]]></search>
|
|
<add position="after"><![CDATA[
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
|
|
$this->load->model('extension/module/img_opti');
|
|
try {
|
|
$this->model_extension_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
|
|
} catch (\Exception $e) {}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
|
|
}
|
|
}]]></search>
|
|
<add position="replace"><![CDATA[copy(DIR_IMAGE . $image_old, DIR_IMAGE . $image_new);
|
|
}
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_wm_status')) {
|
|
$this->load->model('extension/module/img_opti');
|
|
try {
|
|
$this->model_extension_module_img_opti->applyDynamicWatermark(DIR_IMAGE . $image_new, $filename);
|
|
} catch (\Exception $e) {}
|
|
}
|
|
}]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="admin/model/tool/image.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[public function resize($filename, $width, $height) {]]></search>
|
|
<add position="after"><![CDATA[
|
|
$GLOBALS['registry'] = $this->registry;
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_auto_fake_jpg_guard') && !empty($filename)) {
|
|
$this->load->model('extension/module/img_opti');
|
|
try {
|
|
$filename = $this->model_extension_module_img_opti->checkAndFixFakeJpgOnTheFly($filename);
|
|
} catch (\Exception $e) {}
|
|
}
|
|
if (empty($filename) || ($filename !== 'no_image.png' && $filename !== 'placeholder.png' && !is_file(DIR_IMAGE . $filename))) {
|
|
if ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback')) {
|
|
$fallback_image = $this->config->get('module_img_opti_fallback_image');
|
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
|
$filename = $fallback_image;
|
|
}
|
|
}
|
|
}
|
|
if (!empty($filename) && strpos($filename, '.') !== false) {
|
|
$nat_ext = strtolower(substr(strrchr($filename, '.'), 1));
|
|
if ($nat_ext === 'svg' || ($nat_ext === 'webp' && !function_exists('imagewebp'))) {
|
|
$nat_https = isset($this->request->server['HTTPS']) && ($this->request->server['HTTPS'] == 'on' || $this->request->server['HTTPS'] == '1');
|
|
$nat_base = $nat_https ? (defined('HTTPS_CATALOG') ? HTTPS_CATALOG : $this->config->get('config_ssl')) : (defined('HTTP_CATALOG') ? HTTP_CATALOG : $this->config->get('config_url'));
|
|
return $nat_base . 'image/' . str_replace('\\', '/', $filename);
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif')))]]></search>
|
|
<add position="replace"><![CDATA[if (!in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'webp')))]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF)))]]></search>
|
|
<add position="replace"><![CDATA[if (!in_array($image_type, array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF, defined('IMAGETYPE_WEBP') ? IMAGETYPE_WEBP : 18)))]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="system/library/image.php">
|
|
<operation>
|
|
<search><![CDATA[if ($this->mime == 'image/gif') {]]></search>
|
|
<add position="before"><![CDATA[
|
|
if (($this->mime == 'image/webp' || $this->mime == 'image/vnd.webp' || $this->mime == 'image/x-webp') && function_exists('imagecreatefromwebp')) {
|
|
$this->image = @imagecreatefromwebp($file);
|
|
} else
|
|
]]></add>
|
|
</operation>
|
|
<operation>
|
|
<search><![CDATA[if ($extension == 'jpeg' || $extension == 'jpg') {]]></search>
|
|
<add position="before"><![CDATA[
|
|
if ($extension == 'webp' && function_exists('imagewebp')) {
|
|
imagewebp($this->image, $file, $quality);
|
|
} else
|
|
]]></add>
|
|
</operation>
|
|
<operation>
|
|
<search><![CDATA[imagejpeg($this->image, $file, $quality);]]></search>
|
|
<add position="before"><![CDATA[
|
|
@imageinterlace($this->image, true);
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="system/library/response.php">
|
|
<operation>
|
|
<search><![CDATA[echo $output;]]></search>
|
|
<add position="before"><![CDATA[
|
|
$registry = isset($GLOBALS['registry']) ? $GLOBALS['registry'] : null;
|
|
if ($registry && !defined('DIR_CATALOG')) {
|
|
$config = $registry->get('config');
|
|
if ($config && $config->get('module_img_opti_status')) {
|
|
$webp_on_fly = $config->get('module_img_opti_webp_on_fly');
|
|
$lazy_load = $config->get('module_img_opti_lazy_load');
|
|
$broken_fallback = $config->get('module_img_opti_broken_fallback');
|
|
$fallback_image = $config->get('module_img_opti_fallback_image');
|
|
$support_svg = $config->get('module_img_opti_support_svg');
|
|
$support_svg_autofix = ($config->get('module_img_opti_support_svg_autofix') !== null) ? $config->get('module_img_opti_support_svg_autofix') : 0;
|
|
|
|
$theme = $config->get('config_theme');
|
|
$additional_width = 0;
|
|
$additional_height = 0;
|
|
$thumb_width = 0;
|
|
$thumb_height = 0;
|
|
|
|
if ($theme) {
|
|
$additional_width = $config->get('theme_' . $theme . '_image_additional_width');
|
|
$additional_height = $config->get('theme_' . $theme . '_image_additional_height');
|
|
$thumb_width = $config->get('theme_' . $theme . '_image_thumb_width');
|
|
$thumb_height = $config->get('theme_' . $theme . '_image_thumb_height');
|
|
}
|
|
|
|
if (!$additional_width) { $additional_width = $config->get('theme_default_image_additional_width'); }
|
|
if (!$additional_height) { $additional_height = $config->get('theme_default_image_additional_height'); }
|
|
if (!$thumb_width) { $thumb_width = $config->get('theme_default_image_thumb_width'); }
|
|
if (!$thumb_height) { $thumb_height = $config->get('theme_default_image_thumb_height'); }
|
|
|
|
if (!$additional_width) { $additional_width = $config->get('config_image_additional_width'); }
|
|
if (!$additional_height) { $additional_height = $config->get('config_image_additional_height'); }
|
|
if (!$thumb_width) { $thumb_width = $config->get('config_image_thumb_width'); }
|
|
if (!$thumb_height) { $thumb_height = $config->get('config_image_thumb_height'); }
|
|
|
|
if (!$additional_width) { $additional_width = 74; }
|
|
if (!$additional_height) { $additional_height = 74; }
|
|
if (!$thumb_width) { $thumb_width = 228; }
|
|
if (!$thumb_height) { $thumb_height = 228; }
|
|
|
|
if ($support_svg && $support_svg_autofix) {
|
|
$svg_style = '<style>' . "\n" .
|
|
'.product-thumb .image img[src*=".svg"], .product-thumb .image img[src*=".SVG"], ' . "\n" .
|
|
'.thumbnail img[src*=".svg"], .thumbnail img[src*=".SVG"], ' . "\n" .
|
|
'.image-additional img[src*=".svg"], .image-additional img[src*=".SVG"], ' . "\n" .
|
|
'img[src*="/image/catalog/"][src*=".svg"], img[src*="/image/catalog/"][src*=".SVG"] {' . "\n" .
|
|
' max-width: 100% !important;' . "\n" .
|
|
' max-height: 100% !important;' . "\n" .
|
|
' object-fit: contain !important;' . "\n" .
|
|
'}' . "\n" .
|
|
'img.mfp-img[src*=".svg"], img.mfp-img[src*=".SVG"], #cboxLoadedContent img[src*=".svg"] {' . "\n" .
|
|
' width: 800px !important;' . "\n" .
|
|
' max-width: 100% !important;' . "\n" .
|
|
' height: auto !important;' . "\n" .
|
|
'}' . "\n" .
|
|
'.thumbnails img[src*=".svg"], .thumbnails img[src*=".SVG"] {' . "\n" .
|
|
' max-width: 100% !important;' . "\n" .
|
|
' max-height: 100% !important;' . "\n" .
|
|
' width: ' . $thumb_width . 'px !important;' . "\n" .
|
|
' height: ' . $thumb_height . 'px !important;' . "\n" .
|
|
' object-fit: contain !important;' . "\n" .
|
|
'}' . "\n" .
|
|
'</style>';
|
|
$output = str_ireplace('</head>', $svg_style . '</head>', $output);
|
|
}
|
|
|
|
if ($webp_on_fly || $lazy_load || $broken_fallback) {
|
|
$registry->get('load')->model('tool/image');
|
|
$model_tool_image = $registry->get('model_tool_image');
|
|
|
|
if ($webp_on_fly) {
|
|
$output = preg_replace_callback('/<a\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly) {
|
|
$attrs_str = $matches[1];
|
|
preg_match_all('/(\b[\w\-:]+)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))/is', $attrs_str, $attr_matches, PREG_SET_ORDER);
|
|
$attrs = array();
|
|
foreach ($attr_matches as $am) {
|
|
$name = strtolower($am[1]);
|
|
$val = ($am[2] !== '') ? $am[2] : (($am[3] !== '') ? $am[3] : $am[4]);
|
|
$attrs[$name] = $val;
|
|
}
|
|
|
|
if (!empty($attrs['href'])) {
|
|
$href = $attrs['href'];
|
|
$rel_path = '';
|
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png))/is', $href, $m_rel)) {
|
|
$rel_path = urldecode($m_rel[1]);
|
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png))/is', $href, $m_rel)) {
|
|
$rel_path = urldecode($m_rel[1]);
|
|
}
|
|
|
|
$clean_rel_path = preg_replace('/\\?.*/', '', $rel_path);
|
|
$browser_supports_webp = isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false);
|
|
|
|
if ($browser_supports_webp && $clean_rel_path) {
|
|
$ext = strtolower(substr(strrchr($clean_rel_path, '.'), 1));
|
|
$phys_source = DIR_IMAGE . $clean_rel_path;
|
|
$phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $clean_rel_path);
|
|
if (file_exists($phys_source)) {
|
|
if (!file_exists($phys_webp)) {
|
|
$img_data = @file_get_contents($phys_source);
|
|
if ($img_data) {
|
|
$im = @imagecreatefromstring($img_data);
|
|
if ($im) {
|
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
|
$webp_quality = 80;
|
|
}
|
|
@imagewebp($im, $phys_webp, $webp_quality);
|
|
@imagedestroy($im);
|
|
}
|
|
}
|
|
}
|
|
if (file_exists($phys_webp)) {
|
|
$attrs['href'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['href']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$new_attrs = array();
|
|
foreach ($attrs as $k => $v) {
|
|
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
|
|
}
|
|
return '<a ' . implode(' ', $new_attrs) . '>';
|
|
}, $output);
|
|
}
|
|
|
|
$output = preg_replace_callback('/<img\b([^>]*)>/is', function($matches) use ($config, $registry, $model_tool_image, $webp_on_fly, $lazy_load, $broken_fallback, $fallback_image) {
|
|
$attrs_str = $matches[1];
|
|
if (stripos($attrs_str, 'catalog_trash') !== false) {
|
|
return $matches[0];
|
|
}
|
|
|
|
preg_match_all('/(\b[\w\-:]+)\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))/is', $attrs_str, $attr_matches, PREG_SET_ORDER);
|
|
$attrs = array();
|
|
foreach ($attr_matches as $am) {
|
|
$name = strtolower($am[1]);
|
|
$val = ($am[2] !== '') ? $am[2] : (($am[3] !== '') ? $am[3] : $am[4]);
|
|
$attrs[$name] = $val;
|
|
}
|
|
|
|
$src = isset($attrs['src']) ? $attrs['src'] : '';
|
|
$data_src = isset($attrs['data-src']) ? $attrs['data-src'] : '';
|
|
$has_datasrc = ($data_src !== '');
|
|
|
|
$target_url = $has_datasrc ? $data_src : $src;
|
|
$rel_path = '';
|
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
|
$rel_path = urldecode($m_rel[1]);
|
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $target_url, $m_rel)) {
|
|
$rel_path = urldecode($m_rel[1]);
|
|
}
|
|
|
|
$clean_rel_path = preg_replace('/\\?.*/', '', $rel_path);
|
|
$browser_supports_webp = isset($_SERVER['HTTP_ACCEPT']) && (strpos($_SERVER['HTTP_ACCEPT'], 'image/webp') !== false);
|
|
|
|
if ($webp_on_fly && $browser_supports_webp && $clean_rel_path) {
|
|
$ext = strtolower(substr(strrchr($clean_rel_path, '.'), 1));
|
|
if (in_array($ext, array('jpg', 'jpeg', 'png'))) {
|
|
$phys_source = DIR_IMAGE . $clean_rel_path;
|
|
$phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $clean_rel_path);
|
|
if (file_exists($phys_source)) {
|
|
if (!file_exists($phys_webp)) {
|
|
$img_data = @file_get_contents($phys_source);
|
|
if ($img_data) {
|
|
$im = @imagecreatefromstring($img_data);
|
|
if ($im) {
|
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
|
$webp_quality = 80;
|
|
}
|
|
@imagewebp($im, $phys_webp, $webp_quality);
|
|
@imagedestroy($im);
|
|
}
|
|
}
|
|
}
|
|
if (file_exists($phys_webp)) {
|
|
if (isset($attrs['src'])) {
|
|
$attrs['src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['src']);
|
|
}
|
|
if (isset($attrs['data-src'])) {
|
|
$attrs['data-src'] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $attrs['data-src']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($webp_on_fly && $browser_supports_webp) {
|
|
foreach (array('srcset', 'data-srcset') as $srcset_attr) {
|
|
if (isset($attrs[$srcset_attr])) {
|
|
$parts = explode(',', $attrs[$srcset_attr]);
|
|
foreach ($parts as &$part) {
|
|
$part = trim($part);
|
|
$subparts = preg_split('/\\s+/', $part);
|
|
if (!empty($subparts[0])) {
|
|
$s_url = $subparts[0];
|
|
$s_rel = '';
|
|
if (preg_match('/\\/image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
|
$s_rel = urldecode($sm_rel[1]);
|
|
} elseif (preg_match('/^image\\/(.*\\.(?:jpg|jpeg|png|webp|svg))/is', $s_url, $sm_rel)) {
|
|
$s_rel = urldecode($sm_rel[1]);
|
|
}
|
|
$s_clean = preg_replace('/\\?.*/', '', $s_rel);
|
|
if ($s_clean) {
|
|
$s_ext = strtolower(substr(strrchr($s_clean, '.'), 1));
|
|
if (in_array($s_ext, array('jpg', 'jpeg', 'png'))) {
|
|
$s_phys_source = DIR_IMAGE . $s_clean;
|
|
$s_phys_webp = DIR_IMAGE . preg_replace('/\\.(jpg|jpeg|png)$/i', '.webp', $s_clean);
|
|
if (file_exists($s_phys_source)) {
|
|
if (!file_exists($s_phys_webp)) {
|
|
$img_data = @file_get_contents($s_phys_source);
|
|
if ($img_data) {
|
|
$im = @imagecreatefromstring($img_data);
|
|
if ($im) {
|
|
$webp_quality = (int)$config->get('module_img_opti_webp_quality');
|
|
if ($webp_quality <= 0 || $webp_quality > 100) {
|
|
$webp_quality = 80;
|
|
}
|
|
@imagewebp($im, $s_phys_webp, $webp_quality);
|
|
@imagedestroy($im);
|
|
}
|
|
}
|
|
}
|
|
if (file_exists($s_phys_webp)) {
|
|
$subparts[0] = preg_replace('/\\.(jpg|jpeg|png)(\\?|$)/i', '.webp$2', $s_url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$part = implode(' ', $subparts);
|
|
}
|
|
$attrs[$srcset_attr] = implode(', ', $parts);
|
|
}
|
|
}
|
|
}
|
|
|
|
$is_svg = (strtolower(substr(strrchr($clean_rel_path, '.'), 1)) === 'svg');
|
|
|
|
if ($lazy_load && $clean_rel_path && is_file(DIR_IMAGE . $clean_rel_path) && !$is_svg) {
|
|
try {
|
|
$tiny_thumb = $model_tool_image->resize($clean_rel_path, 20, 20);
|
|
if ($has_datasrc) {
|
|
$attrs['src'] = $tiny_thumb;
|
|
} else {
|
|
$attrs['data-src'] = isset($attrs['src']) ? $attrs['src'] : '';
|
|
$attrs['src'] = $tiny_thumb;
|
|
}
|
|
if (isset($attrs['srcset'])) {
|
|
$attrs['data-srcset'] = $attrs['srcset'];
|
|
unset($attrs['srcset']);
|
|
}
|
|
$class = isset($attrs['class']) ? $attrs['class'] . ' img-lazyload' : 'img-lazyload';
|
|
$attrs['class'] = $class;
|
|
|
|
$style = 'filter: blur(10px); transition: filter 0.3s;';
|
|
if (isset($attrs['style'])) {
|
|
$attrs['style'] = rtrim(trim($attrs['style']), ';') . '; ' . $style;
|
|
} else {
|
|
$attrs['style'] = $style;
|
|
}
|
|
} catch (Exception $e) {}
|
|
}
|
|
|
|
if ($broken_fallback) {
|
|
$fallback_url = '';
|
|
$req_obj = $registry->get('request');
|
|
$is_https = isset($req_obj->server['HTTPS']) && ($req_obj->server['HTTPS'] == 'on' || $req_obj->server['HTTPS'] == '1');
|
|
$server_url = $is_https ? $config->get('config_ssl') : $config->get('config_url');
|
|
if ($fallback_image && is_file(DIR_IMAGE . $fallback_image)) {
|
|
$fallback_url = $server_url . 'image/' . $fallback_image;
|
|
} elseif ($config->get('config_logo') && is_file(DIR_IMAGE . $config->get('config_logo'))) {
|
|
$fallback_url = $server_url . 'image/' . $config->get('config_logo');
|
|
} else {
|
|
$fallback_url = $server_url . 'image/no_image.png';
|
|
}
|
|
if ($fallback_url) {
|
|
$attrs['onerror'] = "this.src='" . $fallback_url . "'; this.srcset=''; this.onerror=null;";
|
|
}
|
|
}
|
|
|
|
$new_attrs = array();
|
|
foreach ($attrs as $k => $v) {
|
|
$new_attrs[] = $k . '="' . htmlspecialchars($v, ENT_QUOTES, 'UTF-8') . '"';
|
|
}
|
|
return '<img ' . implode(' ', $new_attrs) . '>';
|
|
}, $output);
|
|
|
|
if ($lazy_load) {
|
|
$lazy_script = '<style>img.img-lazyload-loaded { filter: blur(0) !important; }</style>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
var lazyImages = [].slice.call(document.querySelectorAll("img.img-lazyload"));
|
|
if ("IntersectionObserver" in window) {
|
|
var lazyImageObserver = new IntersectionObserver(function(entries, observer) {
|
|
entries.forEach(function(entry) {
|
|
if (entry.isIntersecting) {
|
|
var lazyImage = entry.target;
|
|
if (lazyImage.dataset.src) {
|
|
lazyImage.src = lazyImage.dataset.src;
|
|
}
|
|
if (lazyImage.dataset.srcset) {
|
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
|
}
|
|
lazyImage.addEventListener("load", function() {
|
|
lazyImage.classList.remove("img-lazyload");
|
|
lazyImage.classList.add("img-lazyload-loaded");
|
|
});
|
|
lazyImageObserver.unobserve(lazyImage);
|
|
}
|
|
});
|
|
});
|
|
lazyImages.forEach(function(lazyImage) {
|
|
lazyImageObserver.observe(lazyImage);
|
|
});
|
|
} else {
|
|
lazyImages.forEach(function(lazyImage) {
|
|
if (lazyImage.dataset.src) {
|
|
lazyImage.src = lazyImage.dataset.src;
|
|
}
|
|
if (lazyImage.dataset.srcset) {
|
|
lazyImage.srcset = lazyImage.dataset.srcset;
|
|
}
|
|
lazyImage.style.filter = "none";
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>';
|
|
$output = str_ireplace('</body>', $lazy_script, $output);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="catalog/controller/product/product.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[if ($product_info['image']) {
|
|
$data['popup'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height'));
|
|
} else {
|
|
$data['popup'] = '';
|
|
}]]></search>
|
|
<add position="replace"><![CDATA[
|
|
if ($product_info['image'] || ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback') && $this->config->get('module_img_opti_fallback_image'))) {
|
|
$data['popup'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_popup_height'));
|
|
} else {
|
|
$data['popup'] = '';
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
<operation error="skip">
|
|
<search><![CDATA[if ($product_info['image']) {
|
|
$data['thumb'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_height'));
|
|
} else {
|
|
$data['thumb'] = '';
|
|
}]]></search>
|
|
<add position="replace"><![CDATA[
|
|
if ($product_info['image'] || ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_broken_fallback') && $this->config->get('module_img_opti_fallback_image'))) {
|
|
$data['thumb'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_width'), $this->config->get('theme_' . $this->config->get('config_theme') . '_image_thumb_height'));
|
|
} else {
|
|
$data['thumb'] = '';
|
|
}
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
|
|
<file path="system/framework.php">
|
|
<operation error="skip">
|
|
<search><![CDATA[$registry = new Registry();]]></search>
|
|
<add position="after"><![CDATA[
|
|
$GLOBALS['registry'] = $registry;
|
|
]]></add>
|
|
</operation>
|
|
</file>
|
|
<file path="system/library/sitecreator/trueFileManager/ControllerCommonTrueFileManager.php">
|
|
<operation error="skip">
|
|
<search><![CDATA['uploadAllow' => array('image/jpeg', 'image/png','image/webp', 'image/gif', 'image/svg+xml',]]></search>
|
|
<add position="replace" offset="2"><![CDATA['uploadAllow' => ($this->config->get('module_img_opti_status') && $this->config->get('module_img_opti_support_webp')) ? array('image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/svg+xml', 'image/pjpeg', 'image/x-png') : array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/pjpeg', 'image/x-png'),]]></add>
|
|
</operation>
|
|
</file>
|
|
<file path="system/library/sitecreator/elFinder/php/elFinderVolumeDriver.class.php">
|
|
<operation error="skip">
|
|
<search><![CDATA['image/vnd.adobe.photoshop' => 'psd']]></search>
|
|
<add position="after"><![CDATA[,
|
|
'image/webp' => 'webp',
|
|
'image/vnd.webp' => 'webp',
|
|
'image/x-webp' => 'webp']]></add>
|
|
</operation>
|
|
</file>
|
|
</modification>
|
|
|