Хак Virtuemart и хаки

Хочу донести недавнее открытие для тех, у кого sh404sef:
При выгрузке товара, чтобы ПС проиндексировали правильные sef урлы, нужно было пройтись по категориям. Так вот если товара выгружалось много и по разным категориям, все это прохождение по ссылкам занимает много времени - напрягает ОЧЕНЬ.
Есть решение:
в адрес пишем Для просмотра ссылки Войди или Зарегистрируйся (показать все товары)
выбираем сортировку по "последние поступления", меняем "по возрастанию" и ставим показ товаров "50".
Далее просто переходим по страницам.

Для того, чтобы самому не щелкать, есть расширение для Firefox и Chrome - Imacros.
Устанавливаем, переходим на сайт, вызываем imacros, нажимаем Record, переходим на следующую страницу на сайте (у меня она "Следующая"), нажимаем Stop. На этом этапе макрос создан, сохраняем его. Далее делаем созданный макрос активным, в поле цикла пишем от 1 до 10000 и жмем "Цикл". идем курить)
 
Хочу донести недавнее открытие для тех, у кого sh404sef:
При выгрузке товара, чтобы ПС проиндексировали правильные sef урлы, нужно было пройтись по категориям. Так вот если товара выгружалось много и по разным категориям, все это прохождение по ссылкам занимает много времени - напрягает ОЧЕНЬ.
Есть решение:
в адрес пишем Для просмотра ссылки Войди или Зарегистрируйся (показать все товары)
выбираем сортировку по "последние поступления", меняем "по возрастанию" и ставим показ товаров "50".
Далее просто переходим по страницам.
Далеко не новость) Но и ПС тут не причем. Лучше бы ты нашел вариант, чтоб ЧПУшки сами делались при появлении нового товара)
А я вот расскажу секрет - чтобы картинки индексились из магазина, правим файл robots.txt (где то было помоему, но напомню) :
User-agent: *
Allow: /components/com_virtuemart/shop_image/category
Allow: /components/com_virtuemart/shop_image/product
Allow: /components/com_virtuemart/shop_image/product/resized
Disallow: /components/
Disallow: /administrator/
Disallow: /cache/
Disallow: /images/
Disallow: /includes/
Disallow: /installation/
Disallow: /language/
Disallow: /libraries/
Disallow: /media/
Disallow: /modules/
Disallow: /plugins/
Disallow: /templates/
Disallow: /tmp/
Disallow: /xmlrpc/
Disallow: /index2.php?page=shop
 
чтобы картинки индексились из магазина, правим файл robots.txt
Тоже баян, но уверен наши советы помогут кому-нибудь.
К robots.txt можно еще добавить такие строки:
Disallow: /*?pop=0
Disallow: /*?pop=1
 
Данных хак позволяет уменьшить количество запросов. Тестировал на J1.5 + VM 1.1.6 заметно уменьшились особенно если используется объемное меню категории.
Открыть administrator\components\com_virtuemart\classes\ps_session.php
Заменить:
Код:
function url($text, $createAbsoluteURI=false, $encodeAmpersands=true, $ignoreSEF=false ) {
        global $mm_action_url, $page, $mainframe;
 
        if(!defined('_VM_IS_BACKEND')) {
 
            // Strip the parameters from the $text variable and parse to a temporary array
            $tmp_text=str_replace('amp;','',substr($text,strpos($text,'?')));
            if(substr($tmp_text,0,1)=='?') $tmp_text=substr($tmp_text,1);
 
            parse_str($tmp_text,$ii_arr);
 
            // Init the temp. Itemid
            $tmp_Itemid='';
 
            $db = new ps_DB;
 
            // Check if there is a menuitem for a product_id (highest priority)
            if (!empty($ii_arr['product_id'])) {
                if ($ii_product_id=intval($ii_arr['product_id'])) {
                    $tmp_Itemid=$this->checkMenuItems('product_id', $ii_product_id);
                }
            }
            // Check if there is a menuitem for a category_id
            // This only checks for the exact category ID, it might be good to check for parents also. But at the moment, this would produce a lot of queries
            if (!empty($ii_arr['category_id'])) {
                $ii_cat_id=intval($ii_arr['category_id']);
                if ( $ii_cat_id && $tmp_Itemid=='') {
                    $tmp_Itemid=$this->checkMenuItems('category_id', $ii_cat_id);
                }
            }
            // Check if there is a menuitem for a flypage
            if (!empty($ii_arr['flypage'])) {
                $ii_flypage=$db->getEscaped(vmget($ii_arr,'flypage'));
                if ($ii_flypage && $tmp_Itemid=='') {
                    $tmp_Itemid=$this->checkMenuItems('flypage', $ii_flypage);
                }
            }
            // Check if there is a menuitem for a page
            if (!empty($ii_arr['page'])) {
                $ii_page=$db->getEscaped(vmget($ii_arr,'page' ));
                if ($ii_page && $tmp_Itemid=='') {
                    $tmp_Itemid=$this->checkMenuItems('page', $ii_page);
                }
            }
            if (!empty($ii_arr['Itemid'])) {
                $ii_itemid=intval($ii_arr['Itemid']);
                if ($ii_itemid && $tmp_Itemid=='') {
                    $tmp_Itemid=$ii_itemid;
                }
            }
            // If we haven't found an Itemid, use the standard VM-Itemid
            $Itemid = "&Itemid=" . ($tmp_Itemid ? $tmp_Itemid : $this->getShopItemid());
 
        } else {
            $Itemid = NULL;
        }
 
        // split url into base ? path
        $limiter = strpos($text, '?');
        if ($limiter === false) {
            if (!strstr($text, "=")) { // $text recognized to be parameter-list (bug?)
                $base = NULL;
                $params = $text;
            }
            else { // text recognized to be url without parameters
                $base = $mm_action_url;
                $params = $text;
            }
        }
        else { // base?params
            $base = substr($text, 0, $limiter);
            $params = substr($text, $limiter+1);
        }
 
        // normalize base (cut off multislashes)
        $base = str_replace("//", "/", $base);
        $base = str_replace(":/", "://", $base);
 
        // add script name to naked base url
        // TODO: Improve
        if ($base == URL || $base == SECUREURL)
            $base .= basename($_SERVER['SCRIPT_NAME']);
        if (!basename($base))
            $base .= basename($_SERVER['SCRIPT_NAME']);
 
        // append "&option=com_virtuemart&Itemid=XX"
        $params .= (!strstr($params, $this->component_name)) ? ($params ? "&" : NULL) . $this->component_name : NULL;
        $params .= $Itemid;
 
        if (vmIsAdminMode() && strstr($text, 'func') !== false)
            $params .= ($params ? "&" : NULL) . 'vmtoken=' . vmSpoofValue($this->getSessionId());
 
        if (!defined( '_VM_IS_BACKEND' )) {
            // index3.php is not available in the frontend!
            $base = str_replace("index3.php", "index2.php", $base);
 
            $url = basename($base) . "?" . $params;
 
            // make url absolute
            if ($createAbsoluteURI && !substr($url, 0, 4 ) != "http") {
                $url = (stristr($text, SECUREURL) ? SECUREURL : URL) . substr($url, $url[0] == '/' ? 1 : 0);
            }
 
            if( class_exists('JRoute') && !$ignoreSEF && $mainframe->getCfg('sef') )
                $url = JRoute::_($url);
            else if( function_exists('sefRelToAbs') && !$ignoreSEF && !defined( '_JLEGACY' ) )
                $url = sefRelToAbs($url);
 
        }
        else // backend
            $url = ($_SERVER['SERVER_PORT'] == 443 ? SECUREURL : URL) . "administrator/" . basename($base) . "?" . $params;
 
        $url = $encodeAmpersands ? vmAmpReplace($url) : str_replace('&', '&', $url);
 
        return $url;
    }
[/spoil]

на этот код:

[spoil]
Код:
function url($text, $createAbsoluteURI=false, $encodeAmpersands=true, $ignoreSEF=false ) {
        global $mm_action_url, $page, $mainframe;
 
        if(!defined('_VM_IS_BACKEND')) {
 
            // Strip the parameters from the $text variable and parse to a temporary array
            $tmp_text=str_replace('amp;','',substr($text,strpos($text,'?')));
            if(substr($tmp_text,0,1)=='?') $tmp_text=substr($tmp_text,1);
 
            parse_str($tmp_text,$ii_arr);
 
            // Init the temp. Itemid
            $tmp_Itemid='';
 
            $db = new ps_DB;
      static $u = null;
      if($u == null){
        $u = array();
        $db->setQuery( "SELECT id, params FROM #__menu WHERE link='index.php?option=com_virtuemart' AND published=1");
        $rows = $db->loadRowList();
 
        foreach($rows AS $row){
          $es = explode("\n", $row[1]);
          foreach($es AS $e){
            $c = explode("=", $e);
            switch($c[0]){
              case 'product_id':
              case 'category_id':
              case 'flypage':
              case 'page':
                if($c[1] != '') $u[$c[0]][$c[1]] = $row[0];
                break;
            }
          }
        }
      }
 
            // Check if there is a menuitem for a product_id (highest priority)
            if (!empty($ii_arr['product_id'])) {
                if ($ii_product_id=intval($ii_arr['product_id'])) {
          if(isset($u['product_id']) && isset($u['product_id'][$ii_product_id])) $tmp_Itemid = $u['product_id'][$ii_product_id];
                }
            }
            // Check if there is a menuitem for a category_id
            // This only checks for the exact category ID, it might be good to check for parents also. But at the moment, this would produce a lot of queries
            if (!empty($ii_arr['category_id'])) {
                $ii_cat_id=intval($ii_arr['category_id']);
                if ( $ii_cat_id && $tmp_Itemid=='') {
          if(isset($u['category_id']) && isset($u['category_id'][$ii_product_id])) $tmp_Itemid = $u['category_id'][$ii_cat_id];
                }
            }
            // Check if there is a menuitem for a flypage
            if (!empty($ii_arr['flypage'])) {
                $ii_flypage=$db->getEscaped(vmget($ii_arr,'flypage'));
                if ($ii_flypage && $tmp_Itemid=='') {
          if(isset($u['flypage']) && isset($u['flypage'][$ii_product_id])) $tmp_Itemid = $u['flypage'][$ii_flypage];
                }
            }
            // Check if there is a menuitem for a page
            if (!empty($ii_arr['page'])) {
                $ii_page=$db->getEscaped(vmget($ii_arr,'page' ));
                if ($ii_page && $tmp_Itemid=='') {
          if(isset($u['page']) && isset($u['page'][$ii_product_id])) $tmp_Itemid = $u['page'][$ii_page];
                }
            }
            // If we haven't found an Itemid, use the standard VM-Itemid
            $Itemid = "&Itemid=" . ($tmp_Itemid ? $tmp_Itemid : $this->getShopItemid());
        } else {
            $Itemid = NULL;
        }
 
        // split url into base ? path
        $limiter = strpos($text, '?');
        if ($limiter === false) {
            if (!strstr($text, "=")) { // $text recognized to be parameter-list (bug?)
                $base = NULL;
                $params = $text;
            }
            else { // text recognized to be url without parameters
                $base = $mm_action_url;
                $params = $text;
            }
        }
        else { // base?params
            $base = substr($text, 0, $limiter);
            $params = substr($text, $limiter+1);
        }
 
        // normalize base (cut off multislashes)
        $base = str_replace("//", "/", $base);
        $base = str_replace(":/", "://", $base);
 
        // add script name to naked base url
        // TODO: Improve
        if ($base == URL || $base == SECUREURL)
            $base .= basename($_SERVER['SCRIPT_NAME']);
        if (!basename($base))
            $base .= basename($_SERVER['SCRIPT_NAME']);
 
        // append "&option=com_virtuemart&Itemid=XX"
        $params .= (!strstr($params, $this->component_name)) ? ($params ? "&" : NULL) . $this->component_name : NULL;
        $params .= $Itemid;
 
        if (vmIsAdminMode() && strstr($text, 'func') !== false)
            $params .= ($params ? "&" : NULL) . 'vmtoken=' . vmSpoofValue($this->getSessionId());
 
        if (!defined( '_VM_IS_BACKEND' )) {
            // index3.php is not available in the frontend!
            $base = str_replace("index3.php", "index2.php", $base);
 
            $url = basename($base) . "?" . $params;
 
            // make url absolute
            if ($createAbsoluteURI && !substr($url, 0, 4 ) != "http") {
                $url = (stristr($text, SECUREURL) ? SECUREURL : URL) . substr($url, $url[0] == '/' ? 1 : 0);
            }
 
            if( class_exists('JRoute') && !$ignoreSEF && $mainframe->getCfg('sef') )
                $url = JRoute::_($url);
            else if( function_exists('sefRelToAbs') && !$ignoreSEF && !defined( '_JLEGACY' ) )
                $url = sefRelToAbs($url);
 
        }
        else // backend
            $url = ($_SERVER['SERVER_PORT'] == 443 ? SECUREURL : URL) . "administrator/" . basename($base) . "?" . $params;
 
        $url = $encodeAmpersands ? vmAmpReplace($url) : str_replace('&', '&', $url);
 
        return $url;
    }
 
Поделюсь своим хаком)

Итак, встала перед нами следующая проблема: компания торгует четырьмя группами товаров (назовем их условно "Группа 1, 2, 3, 4"), и у каждого клиента на каждую группу может быть своя индивидуальная скидка, в то время как VirtueMart 1.1.7a поддерживает только единую скидку на весь товар в магазине.

Что было сделано и допилино:

Через панель администрирования и пункта меню "Настройки" - "Управление полями, заполняемыми пользователем" добавляем четыре новых поля: discount_group_1, 2, 3, 4, тип — text. Префикс "vm" им присвоится автоматически.

Добавляем через phpMyAdmin в таблицу jos_vm_product поле product_group_id, тип varchar(1), где у каждого товара будет храниться код группы, к которой он принадлежит. Тут небольшая ремарка, каким образом это поле заполняется у нас. Выгрузка данных происходит в автоматическом или ручном режиме из 1С:Предприятие 8.1 с помощью самописной обработки в файл форма csv. Именно эта обработка, помимо основных данных о товаре, проверяет также, к какой группе он принадлежит, и выгружает в файл код этой группы (1, 2, 3 или 4). Файл автоматически отправляется по FTP на сайт, где по расписанию CRON выполняет скрипт загрузки или обновления данных в таблице товаров. Если наполнение и редактирование этой таблицы у вас происходит вручную, то нет никаких проблем добавить в админку возможность выводить/править и это дополнительное поле (гугл в помощь, как это сделать =) ).

Создаем отдельный скрипт, который перед выводом цены товара будет проверять его группу, а также скидку клиента на эту группу, если она есть. Вот его текст:


PHP:
if ($user->guest) {
// Пользователь не залогигнен, просто возвращаем базовую цену товара
    return $price;
} else {
// Пользователь залогинен, погнали проверять группы и скидки
 
    // Получаем код группы, к которой принадлежит товар 
    $product_group_id = ps_product::get_field($product_id, "product_group_id");
    // Делаем запрос к таблице с данными пользователя
    $db = new ps_DB;
    $q = "SELECT * FROM #__{vm}_user_info WHERE user_id='". $user->id . "'";
    $db->query($q);
    $db->next_record();
 
    //Присваиваем переменным значения полей со скидками
    $discount_group_1 = $db->f("vm_discount_group_1");
    $discount_group_2 = $db->f("vm_discount_group_2");
    $discount_group_3 = $db->f("vm_discount_group_3");
    $discount_group_4 = $db->f("vm_discount_group_4");
 
    // Проверяем, к какой группе принадлежит товар, и в соответствии с этим присваиваем переменной $skidka размер скидки по этой группе
    switch ($product_group_id){
        case 1:
            $skidka = $discount_group_1;
            break;
        case 2:
            $skidka = $discount_group_2;
            break;
        case 3:
            $skidka = $discount_group_3;
            break;
        case 4:
            $skidka = $discount_group_4;
            break;
        default:
            $skidka = 0;
            break; 
    }
 
    // Теперь проверяем значение поля скидки, если в начале стоит "+", то наоборот делаем наценку на товар (бывает и такое, например, манагер клиента сидит на отстеге =) )
    if (strstr($skidka, "+")){
        str_replace("+", "", $skidka);
        if (is_numeric($price)){
            $price = round($price * (1 + $skidka / 100));
        } else {
            $price['product_price'] = round($price['product_price'] * (1 + $skidka / 100));
        }
    } else {
        if (is_numeric($price)){
            $price = round($price * (1 - $skidka / 100));
        } else {
            $price['product_price'] = round($price['product_price'] * (1 - $skidka / 100));
        }
    }
 
    // Готово! Возвращаем пересчитанную со скидкой и округленную до рубля цену
    return $price;
}
}
 
?>

Записываем этот скрипт под именем check_discount.php и кидаем в каталог administrator/components/com_virtuemart/classes/

Допиливаем под наши нужды функции в файле ps_product.php, который лежит в том же каталоге:

Ищем функцию get_price, которая возвращает цену товара, начинается она вот так:
PHP:
function get_price($product_id, $check_multiple_prices=false, $overrideShopperGroup='' ) {
Сразу после этой строчки прописываем файл с нашей функцей проверки скидки:

PHP:
require_once(CLASSPATH . 'check_discount.php' );


Идем в самый конец функции get_price, где она возвращает цену вот этой строчкой:
PHP:
return $price;


Добавляем перед ней вызов нашей функции проверки скидки:
PHP:
$price = check_discount($product_id, $price);
Теперь нужно запретить пользователю возможность редактировать дополнительные поля со скидками, которые мы создали. В каталоге /administrator/components/com_virtuemart/html/ открываем файл account.billing.php, и ищем следующий блок:

PHP:
// Handle NO_REGISTRATION
$skip_fields = array();
if ( VM_REGISTRATION_TYPE == 'NO_REGISTRATION' || VM_REGISTRATION_TYPE == 'OPTIONAL_REGISTRATION' && empty($d['register_account'])) {
    global $default;
    $default['email'] = $db->f('user_email');
    $skip_fields = array( 'username', 'password', 'password2', );
}

Добавляем сразу после него:

PHP:
//убираем показ этих полей независимо от настроек регистрации
$skip_fields = array( 'vm_discount_group_1', 'vm_discount_group_2', 'vm_discount_group_3', 'vm_discount_group_4' );


Бингонах! )))
 
кто сказал?
Для просмотра ссылки Войди или Зарегистрируйся
Для просмотра ссылки Войди или Зарегистрируйся

ну а за изобретение велосипеда - респект!)))

Это не велосипед) Группа товара-это у нас кожгалантерея, сорочки, чемоданы, трикотаж, которые в свою очередь могут лежать в одной категории. Например сорочки и трикотаж, но скидка на эти группы разная может быть. И как быть с Вашим велосипедом в таком случае?))

P.S.: Магазин завязан, как на рознице, так и на опте. Делалось для оптовиков.
 
Обратил внимание, что хаки все для первой линейки VM. Для второй тоже есть полезности.
Логика работы VirtueMart 2 такова, что метод доставки выбирается автоматически только в том случае, если он всего один.

Если у вас настроено несколько методов доставки, то ни один из них не будет выбран автоматически.
Это значит, что настройка "метода доставки по умолчанию" не предусмотрена (о чем свидетельствует кол-во вопросов на эту тему на форуме разработчиков)

Для того чтобы исправить эту ситуацию, я придумал такой хак:

Нужно изменить файл components\com_virtuemart\helpers\cart.php
Найдите в нем функцию CheckAutomaticSelectedShipment и в конце этой функции найдите такие строки:

PHP:
$this->automaticSelectedShipment=false;
$this->setCartIntoSession();
return false;
и прямо перед ними вставьте этот код:
PHP:
              $preferred_shipment=0; // тут можно поставить любой ID метода доставки, который вы бы хотели использовать по умолчанию
                if ($preferred_shipment==0){ // не используется, если уже определен "ID доставки по умолчанию" на строчке выше
                    if (!class_exists('VirtueMartModelShipmentmethod'))
                        require(JPATH_VM_ADMINISTRATOR.DS.'models'.DS.'shipmentmethod.php');
                    $myship= new VirtueMartModelShipmentmethod;
                    $shipments=$myship->getShipments();
                    if ($shipments){
                        $preferred_shipment = intval($shipments[0]->virtuemart_shipmentmethod_id);
                    }
                }
                if ($this->virtuemart_shipmentmethod_id==0) {
                    $this->virtuemart_shipmentmethod_id=$preferred_shipment;
                    $virtuemart_shipmentmethod_id=$preferred_shipment;}
Смысл исправления: если в коде не выбран метод доставки по умолчанию, то будет выбран первый попавшийся из доступного списка настроенных методов в админке. При этом у посетителя сохраняется возможность поменять способ доставки на любой другой доступный.

Есть файл administrator\components\com_virtuemart\helpers\calculationh.php, в нем также нужно изменить одну строчку.
Найти (у меня на 968 строке) :
Код:
if ($automaticSelectedShipment) $ship_id=$cart->virtuemart_shipmentmethod_id;
И поменять на:
Код:
$ship_id=$cart->virtuemart_shipmentmethod_id;

В чем смысл этого исправления: хелпер проверяет, был ли автоматически выбран ID метода доставки. Функция определения возвращает отрицательный результат (это нужно для того, чтобы сохранить возможность выбора другого способа доставки). И хелпер, не ожидая, что ему вернут готовый ID (как сделали разработчики), на этом останавливается, предоставляя пользователю самому выбрать доставку.

Изменение кода убирает проверку условия "на автоматизм", потому что в нашем случае ID будет передан в любом случае (если у вас конечно настроены методы доставки в админке).

Код не претендует на гениальность, проверялся на VM 2.0.7, на 2.0.8 тоже должен работать.
Для 2.0.6 есть Для просмотра ссылки Войди или Зарегистрируйсярешение (сам не проверял, ибо давно уже на 2.0.8)

Хочу отметить, что раз это хак ядра, то имейте ввиду, что с большой вероятностью он при следующем обновлении затрется.
Однако, другого решения пока видимо не существует.
 
Вопрос по IceVmCart 2.5.1.
Может кто подскажет как в этом модуле сделать так, чтобы при добавлении товара в корзину менялась иконка корзины с пустой на полную?
cart.png
cart-full.png

Здесь я просто изменил изображение скрепки на свое и передвинул в нужную позицию, но изображение "статично", а хотелось бы чтобы оно менялось при добавлении товара в корзину. Подскажите, как такое реализовать?
 
Есть в виртумарте у меня последнем вот такой вывод в категории по количеству на страницу
Для просмотра ссылки Войди или ЗарегистрируйсяДля просмотра ссылки Войди или Зарегистрируйся
как мне туда произвольно добавлять свое количество? например нужно количество на страницу стандартно 15 поставить и чтобы там тоже было 15
 
Назад
Сверху