Самый популярный движок интернет-магазина для Joomla.

Как устроена генерация миниатюр в Virtuemart 1.xx

alex » 15 июн 2013, 00:06

Здесь я собираюсь окончательно разобраться с проблемами, которые связаны с такими понятиями как:
Это действительно одна из самых мучительных тем для всех разработчиков на Virtuemart 1.xx и похоже в наследство доставшаяся и Virtuemart 2.xx, судя по обзору русскоязычных форумов и http://forum.virtuemart.net/, на котором тоже нет адекватных пояснений.
Когда я решил досконально разобраться со всеми вопросами, связанными с изображениями товаров и категорий в Virtuemart, то думал что примерно после недели изучения я напишу детальную статью, в которой разложу все по полочкам, но..
Похоже что алгоритмы работы с изображениями писала группа людей, которые плохо между собой взаимодействовали и писали код на протяжении достаточно долгого периода времени, урывками, и никто не имел точного представления о том как работает код в целом, в общем там беда с рефакторингом и логикой..
Я понял, что если попытаться все детально здесь расписать, то это будут несколько страниц, в которых любой человек запутается.
Поэтому я излагаю здесь основные моменты работы, а если кому-то интересны нюансы, то смотрите детали в нижеприведенных исходниках, это действительно тот случай, когда детали очень важны, и без них никуда.
Вот список основных файлов кода Virtuemart 1.xx, которые принимают участие в генерации миниатюр и полных изображений:

Код: Выделить всё
/administrator/components/com_virtuemart/classes/class.img2thumb.php
/administrator/components/com_virtuemart/classes/htmlTools.class.php
/administrator/components/com_virtuemart/classes/imageTools.class.php
/administrator/components/com_virtuemart/classes/ps_product.php
/administrator/components/com_virtuemart/classes/ps_product_category.php
/administrator/components/com_virtuemart/classes/ps_product_files.php
/components/com_virtuemart/show_image_in_imgtag.php
/components/com_virtuemart/themes/default/templates/common/theme.php
/components/com_virtuemart/themes/default/templates/browse/browse_1.php
/components/com_virtuemart/themes/default/templates/product_details/flypage.tpl.php


Ещё раз хочу уточнить: в данном посте я рассматриваю версию магазина "VirtueMart 1.1.5" это имеет значение т. к. логика работы с изображениями там не последовательна, отдельные подзадачи типа генерации миниатюр для основного изображения товара, генерация миниатюр для дополнительных изображений товара, генерация миниатюр для категорий - все они живут своей особенной жизнью. В других версиях Virtuemart могут быть незначительные отличия.

Ну, что ж, давайте по порядку.

1. Опция "Включить динамическое изменение размеров для мини-изображения".
Эта опция делает так, что при обращении к странице магазина превьюшки отдаются не как путь к файлу на сервере, а как путь к генерирующему скрипту вида "http://vm-test/components/com_virtuemart/show_image_in_imgtag.php?filename=_________________519e9fe7af0a5.jpg&newxsize=120&newysize=120&fileout=".
Суть скрипта такова, что если превьюшка заданного размера существует, то она отдается (функцией readfile), а если файла такого нет, то он генерируется, сохраняется на сервере с остальными миниатюрами, и сразу считывается и так же отдается. Таким образом миниатюры генерируются только один раз при первом просмотре страницы, потом они уже будут существовать как реальные файлы.
То что название файла предваряется множеством символов подчеркивания, - это при сохранении магазин пытается использовать название товара или категории, но все не латинские символы (каждый байт) он заменяет знаком подчеркивания, а в кодировке UTF-8, как известно, символ кириллицы кодируется двумя байтами. А потом вся эта последовательность символов используется как префикс для функции "uniqid".

И так, при выборе этой опции:

1.1. На иконы категорий по умолчанию эта опция влияния не оказывает, но если в файле шаблона, например,
/components/com_virtuemart/themes/default/templates/common/categoryChildlist.tpl.php
вместо
Код: Выделить всё
echo ps_product::image_tag( $category["category_thumb_image"], "alt=\"".$category["category_name"]."\"", 0, "category");
в третьем параметре задать "1"
Код: Выделить всё
echo ps_product::image_tag( $category["category_thumb_image"], "alt=\"".$category["category_name"]."\"", 1, "category");
, то иконы категорий примут новые размеры.

1.2. На иконы изображений товаров в категории, если они созданы в админке путем автоматической генерации при загрузке большого изображения, эта опция влияния не оказывает.
Но, если в файле шаблона, например, /components/com_virtuemart/themes/default/templates/browse/browse_1.php в строке
Код: Выделить всё
document.write( '<?php echo ps_product::image_tag( $product_thumb_image, 'class="browseProductImage" border="0" title="'.$product_name.'" alt="'.$product_name .'"' ) ?></a>' );
вместо $product_thumb_image задать $product_full_image
Код: Выделить всё
document.write( '<?php echo ps_product::image_tag( $product_full_image, 'class="browseProductImage" border="0" title="'.$product_name.'" alt="'.$product_name .'"' ) ?></a>' );

и в файле:
/administrator/components/com_virtuemart/html/shop.browse.php
найти примерно на 380 строке:
Код: Выделить всё
$product_full_image = IMAGEURL . 'product/' . $product_full_image;
и закомментировать её
Код: Выделить всё
// $product_full_image = IMAGEURL . 'product/' . $product_full_image;
,то миниатюры основных изображений товара при просмотре категории начнут динамически ресайзиться.

1.3. На иконы основного изображения товаров в карточке товара эта опция влияния не оказывает.
Но, если в файле:
/components/com_virtuemart/themes/default/templates/common/theme.php
в районе 85 строки найти:
Код: Выделить всё
$text = ps_product::image_tag($product['product_thumb_image'], $img_attributes, 1)."<br/>".$VM_LANG->_('PHPSHOP_FLYPAGE_ENLARGE_IMAGE');
и заменить $product['product_thumb_image'] на $product['product_full_image']
Код: Выделить всё
$text = ps_product::image_tag($product['product_full_image'], $img_attributes, 0)."<br/>".$VM_LANG->_('PHPSHOP_FLYPAGE_ENLARGE_IMAGE');
,то превьюшки основного изображения товара в карточке товара начнут динамически ресайзиться.

1.4. На иконы дополнительных изображений товара в карточке товара эта опция влияние оказывает и иконы дополнительных изображений товара генерируются новых размеров, но визуально в браузере это не видно т. к. в тег img вставляются аттрибуты высоты и ширины, соответствующие миниатюре, изначально сгенерированной при загрузке дополнительного изображения товара.
Для того чтобы это исправить, нужно в файле:
/components/com_virtuemart/themes/default/templates/common/theme.php
В районе строки 117 найти:
Код: Выделить всё
$thumbtag = ps_product::image_tag( $image->file_name, 'class="browseProductImage"', 1, 'product', $image->file_image_thumb_width, $image->file_image_thumb_height );
и удалить параметры $image->file_image_thumb_width, $image->file_image_thumb_height
Код: Выделить всё
$thumbtag = ps_product::image_tag( $image->file_name, 'class="browseProductImage"', 1 );
, наверное, прочитав вышеприведенный рецепт, вы подумали: "ааа, ну да, просто не будут выводиться атрибуты width и height в теге img ", тогда откройте код и вы увидите, что атрибуты выводятся, но с правильными значениями.

2. Дублирование миниатюр товара в каталог (папку) "products".
При загрузке основного изображения товара и атоматическом создании миниатюры происходит также сохранение миниатюры товара в каталог (папку) "products", - это все следствие вышеописанных причин (непродуманности кода). В последних версиях Virtuemart этот баг исправлен, собственно, рецепт я таким образом и нашел, просмотрев исходники.
В частности, открываем файл:
/administrator/components/com_virtuemart/classes/ps_product.php
В районе строки 125 находим:
Код: Выделить всё
      else {
         // File Upload
         if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
            $valid = false;
         }
      }

заменяем этот блок кода на:
Код: Выделить всё
      else {
         // File Upload
         if ($d['product_full_image_action'] == 'auto_resize' &&  is_uploaded_file($_FILES['product_full_image']["tmp_name"]) ) {
            //do nothing, as uploaded image will be resized below
         }
         else {
            if (!vmImageTools::validate_image($d,"product_thumb_image","product")) {
               $valid = false;
            }
         }
      }

Теперь лишние бесполезные копии миниатюр создаваться не будут.

3. Пропали дополнительные изображения товара при переносе сайта на Joomla 1.5.xx и VirtueMart 1.1.x .
Если для вывода дополнительных изображений товара вы используете эффект "LightBox" и при этом переносите сайт с одного хостинга на другой, то следует помнить о таком подводном камне, что url-адрес полного изображения будет вести на предыдущий хост т. к. он жестко прописан в поле "file_url" таблицы "jos_vm_product_files" (естественно, префикс jos_ может у вас быть и другим).
Здесь я вижу по крайней мере 2 варианта решения этой проблемы:
1-й вариант: открываем файл
/components/com_virtuemart/themes/default/templates/common/theme.php
В районе строки 120 находим:
Код: Выделить всё
         if( $this->get_cfg('useLightBoxImages', 1 )) {
            $html .= vmCommonHTML::getLightboxImageLink( $image->file_url, $thumbtag, $title ? $title : stripslashes($image->file_title), 'product'.$product_id );
         }
         else {
            $html .= vmPopupLink( $fulladdress, $thumbtag, 640, 550 );
         }

И превращаем абсолютный url полного изображения ($image->file_url) в относительный, вот так:
Код: Выделить всё
         if( $this->get_cfg('useLightBoxImages', 1 )) {
            $html .= vmCommonHTML::getLightboxImageLink( str_replace(URL, '/' , $image->file_url), $thumbtag, $title ? $title : stripslashes($image->file_title), 'product'.$product_id );
         }
         else {
            $html .= vmPopupLink( $fulladdress, $thumbtag, 640, 550 );
         }

2-й вариант - это заменить вручную или SQL-запросом этот url в базе (таблица "jos_vm_product_files" поле "file_url").

Ну и чтобы на корню пресечь это безобразие с абсолютными адресами дополнительных изображений товара, открываем файл
/administrator/components/com_virtuemart/classes/ps_product_files.php
В районе строки 527 находим:
Код: Выделить всё
      switch( @$d['file_type'] ) {
         case 'image':
         case 'product_images':
         case 'product_full_image':
         case 'product_thumb_image':
            
            $d['is_image'] = "1";
            $d["file_url"] = IMAGEURL."product/".$d['filename'];

И заменяем "IMAGEURL" на "/components/com_virtuemart/shop_image/product/":
Код: Выделить всё
      switch( @$d['file_type'] ) {
         case 'image':
         case 'product_images':
         case 'product_full_image':
         case 'product_thumb_image':
            
            $d['is_image'] = "1";
            $d["file_url"] = "/components/com_virtuemart/shop_image/product/".$d['filename'];

Вообще, поначалу я пошел другим путём и поменял в файле
/administrator/components/com_virtuemart/virtuemart.cfg.php
строку
define( 'IMAGEURL', URL.'components/com_virtuemart/shop_image/' );
на
define( 'IMAGEURL', '/components/com_virtuemart/shop_image/' );
И то же самое нужно сделать и в файле
/administrator/components/com_virtuemart/classes/ps_config.php
иначе при редактировании настроек в админ-панели магазина этот параметр в файле "virtuemart.cfg.php" будет перезаписан на дефолтный из "ps_config.php".
Этот метод конечно сработал, но появились нюансы с отображением миниатюр главных и дополнительных изображений товара и изображений категории, в частности, они при отключенной опции "Включить динамическое изменение размеров для мини-изображения" принимали размеры 100*100px. и т. п.
В принципе это фиксится в файле
/administrator/components/com_virtuemart/classes/ps_product.php
В районе строки 1401 находим:
Код: Выделить всё
if( file_exists($image)) {
                  $url = str_replace( $mosConfig_absolute_path, $mosConfig_live_site, $image );
                  
               } elseif( file_exists($mosConfig_absolute_path.'/'.$image)) {
                  $url = $mosConfig_live_site.'/'.$image;
               }

И меняем " $mosConfig_live_site" на пустую строку "" :
Код: Выделить всё
if( file_exists($image)) {
                  $url = str_replace( $mosConfig_absolute_path, "", $image );
                  
               } elseif( file_exists($mosConfig_absolute_path.'/'.$image)) {
                  $url = $mosConfig_live_site.'/'.$image;
               }

Но я подумал что мог не всё учесть с этой константной 'IMAGEURL", да и надоело возиться с этим кодом, и проще вариант с правкой одной строки в
/administrator/components/com_virtuemart/classes/ps_product_files.php
как выше описал уже.

4. Не создаются миниатюры дополнительных изображений товара, если загружаемая картинка имеет расширение "jpeg" (именно "jpeg", а не "jpg").
При загрузке дополнительных изображений товара, если расширение файла ".jpeg", то к имени файла генерируемой миниатюры будет добавляться еще окончание ".jpg". Т. е. в папку resized будет класться файл вида file-name.jpeg.jpg а в базу будет записываться имя вида "file-name.jpeg". Эта проблема присутствует и в Virtuemart 2.xx.
Дело в файле
/administrator/components/com_virtuemart/classes/class.img2thumb.php
В районе строки 296 находим код:
Код: Выделить всё
         case "jpg":
            if (strtolower(substr($fileout,strlen($fileout)-4,4))!=".jpg")
               $fileout .= ".jpg";
            return imagejpeg($new_img, $fileout, 100);
            break;

Добавляем еще одно условие: strtolower(substr($fileout,strlen($fileout)-5,5))!=".jpeg"
Код: Выделить всё
         case "jpg":
            if (strtolower(substr($fileout,strlen($fileout)-4,4))!=".jpg" && strtolower(substr($fileout,strlen($fileout)-5,5))!=".jpeg")
               $fileout .= ".jpg";
            return imagejpeg($new_img, $fileout, 100);
            break;

Кстати, на форуме virtuemart.net я видел другое странное решение этой проблемы, которое потом и в рунете несколько раз втречал, это типичный "Индусский код", достойно цитирования, интересно, как улюдей голова устроена, когда такое предлагают:
Код: Выделить всё
case "jpg":
if (strtolower(substr($fileout,strlen($fileout)-4,4))!=".jpg" && strtolower(substr($fileout,strlen($fileout)-9,9))!=".jpeg.jpg")
$fileout .= ".jpg";
if (strtolower(substr($fileout,strlen($fileout)-9,9))==".jpeg.jpg")
$fileout = preg_replace('/\.jpg$/i','',$fileout);
return imagejpeg($new_img, $fileout, 100);
break;
alex
Администратор
 
Сообщения: 53
Зарегистрирован: 17 апр 2010, 00:45

Вернуться в Virtuemart