Перейти к содержимому


Highest Reputation Content


#34208 Ускоряем работу клиентской части ShopCMS при большом количестве товаров и кат...

Написано badisoft на 20 January 2016 - 03:37 PM

Ускоряем работу клиентской части ShopCMS
с большим количеством каталогов (порядка тысячи и более),
большим количеством товаров (десятки тысяч и более)
и большим количеством зарегистрированных клиентов (порядка нескольких тысяч и более).

Количество категорий и товаров можно посмотреть в "Админка->Отчеты->Системные данные".
Количество зарегистрированных клиентов в "Админка->Списки->Покупатели",
там умножить число строк (обычно это 20) на число страниц.


Программная часть (скорость выполнения PHP-кода)
================================================

При тысяче (и более) категорий основное время PHP-вычислений занимает построение иерархического списка категорий, который мы видим в блоке "Развернутый каталог" и на странице "Расширенный поиск".
При каждой перезагрузке страницы таблица категорий заново получается SQL-запросом из базы и на нее напускается рекурсивная функция построения списка, которая (при тысяче категорий/подкатегорий) работает долго.
Но зачем каждый раз заново строить список категорий в клиентской части, если изменяется этот список только в админке? Куда правильнее сохранить этот список готовым в файл и брать его каждый раз оттуда, меняя этот файл только из админки.
Примерно так и сделано.
В клиентской части: если файлов с таблицей категорий и построенным списком нет, то все происходит штатным путем (медленно), а таблица категорий и построенный список сохраняются в файлы. Т.е. на следующей странице они будут взяты уже из файлов (быстро).
В админской части: если произошли изменения в таблице категорий, то файлы удаляются и - см.выше.
Можно прямо в админке их и создать заново, но особого смысла я не вижу. Просто у первого клиента после изменений в категориях медленнее загрузится первая страница.
Второй список категорий (для "не развернутого" каталога) не трогался, т.к. строится линейно и быстро.


1. в файле category_functions.php

1.1. в функции catGetCategoryCListMin

вместо


return _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);


вставляем


# BEGIN кэшируем в файл клиентский список категорий
#return _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);
if (file_exists("core/temp/catcache.txt") && ($cache = file_get_contents("core/temp/catcache.txt")) && $cats = unserialize($cache)) return $cats;
else
{
$cats = _recursiveGetCategoryCList( 1, 0, null, 'NUM', false, false);
file_put_contents("core/temp/catcache.txt",serialize($cats));
return $cats;
}
# END кэшируем в файл клиентский список категорий


1.2. в функции update_psCount в начало вставляем


# BEGIN кэшируем в файл клиентский список категорий
if (file_exists("core/temp/catcache.txt")) unlink("core/temp/catcache.txt");
if (file_exists("core/temp/categories.txt")) unlink("core/temp/categories.txt");
# END кэшируем в файл клиентский список категорий



2. в файле index.php

2.1. перед


$q = db_query("select categoryID, name, products_count, products_count_admin, parent, picture, subcount FROM ".CATEGORIES_TABLE." ORDER BY sort_order, name");


вставляем


# BEGIN кэшируем в файл клиентский список категорий
if (file_exists("core/temp/categories.txt") && ($cache = file_get_contents("core/temp/categories.txt")) && $categories = unserialize($cache))
{
foreach ($categories as $row)
{
$fc[(int)$row["categoryID"]] = $row;
$mc[(int)$row["categoryID"]] = (int)$row["parent"];
}
}
else
{
$categories = array();
# END кэшируем в файл клиентский список категорий


2.2. после


$mc[(int)$row["categoryID"]] = (int)$row["parent"];


вставляем


# BEGIN кэшируем в файл клиентский список категорий
$categories[] = $row;
}
file_put_contents("core/temp/categories.txt",serialize($categories));
# END кэшируем в файл клиентский список категорий




SQL-часть (скорость выполнения запросов)
========================================

У таблиц категорий, товаров и клиентов довольно небольшой список используемых вариантов сортировки и полей для поиска.
Достаточно добавить несколько индексов, чтобы на удивление сильно увеличить скорость выполнения некоторых тормозных запросов.

создаем файл addon.php с содержимым


<?php
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX date_added(date_added)"); // home.php - новинки
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX items_sold(items_sold)"); // home.php - популярные

db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX sort_order_name(sort_order,name)"); // стандартная сортировка товара
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX price(Price)"); // сортировка по цене товара
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX enabled(enabled)"); // включенные-отключенные товары
db_query("ALTER TABLE ".PRODUCTS_TABLE." ADD INDEX name(name)"); // сортировка по названию товара

db_query("ALTER TABLE ".CATEGORIES_TABLE." ADD INDEX sort_order_name(sort_order,name)"); // стандартная сортировка категорий

db_query("ALTER TABLE ".CUSTOMERS_TABLE." ADD INDEX login(Login)"); // для WHERE Login=

unlink("core/includes/admin/addon.php");
?>


Кладем его в core/includes/admin/ и заходим в админку либо перезагружаем страницу, если уже в админке.
Файл добавит индексы и должен удалиться сам, но лучше проверить (мне попадались сайты, где удаление файлов в этом каталоге было запрещено).


В общем случае я искал тормозные запросы следующим образом:

В файле mysql.php в функции db_query

строку


$res["resource"] = mysql_query($s);


заменил на


if (isset($_GET['speedtest'])) global $relaccess;list($usec, $sec) = explode(" ", microtime());$start = (float)$usec + (float)$sec;
$res["resource"] = mysql_query($s);
if (isset($_GET['speedtest'])) if (in_array(100, $relaccess)) {list($usec1, $sec1) = explode(" ", microtime());$f=fopen('sql.txt','a');fwrite($f,round((float)$usec1+(float)$sec1-$start,5)."/".$s."\n");fclose($f);}


При этом если находишься под админом и добавляешь в url строку с GET-переменной speedtest=1 (например, http://badisoft.ru/?speedtest=1), то в корне сайта создастся (либо будет дополняться, если уже есть) файл sql.txt примерно с таким содержимым:

0.00021/select code, currency_value, where2show, currency_iso_3, Name, roundval from emph_currency_types where CID=3
0.0002/select CID, Name, code, currency_value, where2show, roundval, currency_iso_3 from emph_currency_types order by sort_order
0.01132/select categoryID, name, products_count, products_count_admin, parent, picture, subcount FROM emph_categories ORDER BY sort_order, name
0.00056/select bid, title, content, bposition, which, sort, html, url, admin, pages, dpages, categories, products FROM emph_blocks WHERE active=1 ORDER BY sort ASC
0.00021/select count(*) from emph_categories where categoryID=1
0.0002/select categoryID, parent, name FROM emph_categories WHERE categoryID=1
0.00051/select categoryID, parent, name, products_count from emph_categories where parent=1  order by sort_order, name
0.00023/select last_update from emph_dump where type=1
0.00022/select today from emph_counter WHERE tbid=1
0.05832/replace into emph_online values ('97ca250a247dba98302e15d5f127644a', '1453217907')
0.0584/UPDATE emph_counter SET todayp=todayp+0, todayv=todayv+1, allp=allp+0, allv=allv+1, allieb=allieb+0, allmozb=allmozb+1, allopb=allopb+0, allozb=allozb+0, allrusl=allrusl+1, allenl=allenl+0, allozl=allozl+0, allwins=allwins+1, alllins=alllins+0, allmacs=allmacs+0, allozs=allozs+0 WHERE tbid=1
0.00096/select count(*) from emph_online WHERE time > 1453217307
0.00023/select todayp, todayv, allp, allv from emph_counter WHERE tbid=1
0.00019/select poll_id, poll_title, poll_ans, all_poll FROM emph_survey WHERE active=1
[тут кусок лога вырезан для краткости]
0.00024/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT'
0.00025/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT'
0.00701/select customerID from emph_customers where Login='admin'
0.00021/select itemID, Quantity FROM emph_shopping_carts WHERE customerID=1
0.0003/select orderID, customerID, order_time, customer_ip,  shipping_type, payment_type, customers_comment,  statusID, shipping_cost, order_discount, order_amount,  currency_code, currency_value, customer_firstname, customer_lastname,  customer_email, shipping_firstname, shipping_lastname,  shipping_country, shipping_state, shipping_city,  shipping_address, billing_firstname, billing_lastname, billing_country,  billing_state, billing_city, billing_address,  cc_number, cc_holdername, cc_expires, cc_cvv, affiliateID, shippingServiceInfo, currency_round  from emph_orders where orderID=0
0.00018/select AID, uri, update_date, title, textToPrePublication, bid from emph_articles order by ordering,update_date DESC,AID DESC
0.00025/select settings_value from emph_settings where settings_constant_name='CONF_DATE_FORMAT'

Что мы видим? Видим кучу запросов по 0.000xx секунд и четыре явно медленных запроса 0.01132, 0.05832, 0.0584, 0.00701.
Первый решается индексом этой таблицы (категорий) по sort_order+name.
Второй и третий - это запросы малюсеньких таблиц (в третьм таблица вообще в одну запись), но с изменением содержимого таблиц, тут был не лучшим образом настроен SQL-сервер.
Четвертый решается индексом этой таблицы (клиентов) по Login.

Вот таким не особенно сложным способом скорость работы сайта была улучшена с

Обработка данных: 0.350 сек
Работа с БД: 0.260 сек
Общее время работы: 0.665 сек

до

Обработка данных: 0.041 сек
Работа с БД: 0.011 сек
Общее время работы: 0.056 сек

PS. В процессе улучшений случился также и переезд на другое железо, но, как ни странно, особого прироста производительности это не дало. Хотя "стало в два раза быстрее" - это тоже хорошо.
Наибольший прирост общей скорости работы дали:
1. кэширование на диск списка категорий.
2. настройка MySQL-сервера. Исходно ооочень медленно, на порядок-два хуже SELECT-ов выполнялись запросы, меняющие содержимое таблиц - UPDATE, REPLACE, INSERТ. Что-то связанное с кэшированием изменений и сбросом их на диск. Делал не я. Я в настройках MySQL-сервера ничего не понимаю. Видимо, какая-то специфика развернутого сервера, т.к. у меня все MySQL-сервера стоят с настройками практически по дефолту, но такой разницы между SELECT-ом и UPDATE-ом нет.
3. создание индексов по часто используемым полям у объемных таблиц (категории, товары, клиенты).


PPS. Обязательно надо мониторить в админке раздел "Статистика ошибок" и следить, чтобы там не было записей.
Особенно если в лог валятся не только ошибки, но и warning по несоответствию типа данных, чего даже в штатном shopCMS "из коробки" довольно много.
При появлении error или warning происходит:
1. вставка сообщения в таблицу (INSERT)
2. подсчет записей в таблице (SELECT)
3. усечение таблицы, если записей больше пятидесяти (DELETE)
т.е. при периодическом warning, который никак не влияет на работоспособность сайта мы получим три лишних SQL-запроса по каждому warning, причем два из них могут оказаться не быстрыми.

Некоторый (довольно небольшой) прирост даст убивание сбора статистики по клиентам (файл counter.php, но осторожно, там еще и ежесуточная чистка таблицы сессий).
Просто станет на пять SQL-запросов меньше.
Статистика там все равно довольно бессмысленная (браузеры - IE/FF/Opera/Other, ОС - Win/Lin/Mac/Other, Языки - Ru/En/Other).
Новых браузеров нет, новых ОС нет.
Статистика Яндекса или Гугля (а обычно у сайта есть обе) покажут то же самое намного лучше и развесистей.
  • 7


#25878 Слайдер баннеров для ShopCMS

Написано zcz на 18 March 2013 - 01:43 PM

Давно ничего не писал, т.к. времени в обрез.
Представляю вашему вниманию новый модуль для организации слайдера.

О модуле:
Данный модуль позволяет создать на сайте слайдер изображения, с возможностью выставления ссылки для определенной картинки и описательного текста.

Модуль писался под заказ.

Прикрепленный файл  slider_by_zcz.rar   1.92МБ   129 Количество загрузок:

пример работы: shotshop.ru
  • 7


#18727 Модуль "Быстрый заказ" by vsupport.club

Написано R.Sergey на 16 April 2012 - 01:24 AM

С чего все началось )))) заказчик заказал установку модуля "Быстрый заказ" .... )) но это не модуль а убожество какое-то, поэтому решил сегодня написать свой, человеческий.
Получилось вроде неплохо, хотя еще есть кое-что что не мешало бы доделать.

Прикрепленный файл  screen.png   615.3К   67 Количество загрузок:

Ну и собственно демонстрация

http://www.youtube.com/watch?v=O_og9Kjl_Yk

Скачать модуль "Быстрый заказ" by vsupport.club
  • 7


#35004 Авторизация/регистрация через соц.сети (uLogin)

Написано zcz на 03 November 2016 - 08:22 PM

Модуль авторизации/регистрации через соц.сети с помощью uLogin для ShopCMS 3.1.*

ВСЕ ГОВОРИМ СПАСИБО stasikons

Собственно и говорить не о чем.
Модуль дает возможность регистрироваться и авторизовываться через соц.сети с помощью сервиса uLogin. ru

Усановка:

Заливаем файлы по своим папкам.

!ВКЛЮЧАЕМ ПРИНУДИТЕЛЬНУЮ ОЧИСТКУ КЕША!

Открываем index.php и находим строчку

if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file" ))) {

в конце, после
"get_file"
вставляем
, "ulogin"

открываем core/includes/admin/modules.php

перед строкой

array("id"=>"yandex", "name"=>"Яндекс.Маркет" )

ставим

//uLogin
array("id"=>"ulogin", "name"=>"Настройки uLogin"),
//END uLogin

открываем core/tpl/admin/menu.tpl.html и перед строкой

<table class="adn topj"><tr><td><img src="data/admin/drs.gif" alt=""></td><td width="100%"><a href="{$smarty.const.ADMIN_FILE}?dpt=modules&amp;sub=yandex">{$smarty.const.ADMIN_STRING_YANDEX}</a></td></tr></table>

вставляем

{*uLogin*}
<table class="adn topj"><tr><td><img src="data/admin/drs.gif" alt=""></td><td width="100%"><a href="{$smarty.const.ADMIN_FILE}?dpt=modules&amp;sub=ulogin">uLogin</a></td></tr></table>
{*END uLogin*}

На удивление, но работа с файлами закончена.
Теперь необходимо зарегистрироваться (если еще не сделали этого) в системе uLogin.ru
Что вам необходимо от этой системы - регистрация, подтверждение сайта и получение уникального ID для работы модуля.
Это я расписывать не буду - там все интуитивно понятно.
После подтверждения сайта и получения уникального ID (выглядит он примерно так 123ba45b), заходим в админку и кликаем по ссылке модуля для его установки и настройки.

Появляться на сайте uLogin, в принципе, нет никакой нужды, т.к. вся настройка модуля (кнопки, язык, доп.поля, и внешний вид) происходит уже у вас в админке.

Наслаждайтесь!

Прикрепленный файл  uLogin.rar   53.37К   14 Количество загрузок:

При регистрации берутся логин, имя, фамилия, емейл. Если каких-то из данных не хватает в соц.сети, то uLogin сам предлагает их заполнить.
доп.поля город и страна подключаются в админке (если не включены, то не записываются в базу)
т.е. после регистрации, клиент получает полный доступ ко всем функциям сайта и в дальнейшем может проходить авторизацию как через соц.сеть, так и по логину и паролю.
  • 6


#31845 Штатные ошибки ShopCMS, из коробки :).

Написано badisoft на 09 November 2014 - 03:57 PM

Решил собрать в кучу и худо-бедно причесать найденные в ShopCMS штатные ошибки и странности:

================================================
Штатная ошибка. Из-за нее незарегистрированный пользователь не сможет скачать купленный им электронный товар.

В файле core/includes/processor/get_file.php
строку:


$q = db_query("select order_time from ".ORDERS_TABLE." where orderID=".orderID);


нужно заменить на:


$q = db_query("select order_time from ".ORDERS_TABLE." where orderID=".$orderID);


P.S. Чтобы незарегистрированный пользователь мог получить ссылку на электронный товар, нужно в общих настройках админ-панели поставить галочку напротив пункта E-mail отчеты для покупателей

=====================

Нашел ошибку в catalog_extra.php (штатную). Перепутаны таблицы, это вызывает ошибку SQL при удалении варианта характеристики.
Исправление:


#db_query("delete from ".CATEGORY_PRODUCT_OPTIONS_TABLE." where variantID=".$variantid);
db_query("delete from ".CATEGORY_PRODUCT_OPTION_VARIANTS." where variantID=".$variantid);


=============================

Штатная ошибка: При импорте из CSV варианты характеристик, содержащие символы '"<>& двоятся, т.е. создаются столько раз, сколько товаров с таким вариантом характеристики.

Исправление: в функции optOptionValueExists строку


" where optionID=".(int)$optionID." and option_value='".xEscSQL(trim($value_name))."';");


заменяем на


#" where optionID=".(int)$optionID." and option_value='".xEscSQL(trim($value_name))."';");
" where optionID=".(int)$optionID." and option_value='".xToText(trim($value_name))."';");



=============================

Есть в discount_functions.php функция _calculateGeneralPriceDiscount, возвращающая размер скидки в зависимости от суммы заказа. Выглядит она вот так:


function _calculateGeneralPriceDiscount( $orderPrice, $log )
{
$customerID = (int)regGetIdByLogin($log);
$q = db_query("select discount_id, price_range, percent_discount from ".
ORDER_PRICE_DISCOUNT_TABLE." order by price_range " );
$data = array();
while( $row = db_fetch_row($q) ) $data[] = $row;

if ( count($data) != 0 )
{
for( $i=0; $i<count($data)-1; $i++ )
{
if ( $data[$i][ "price_range" ] < $orderPrice
&& $orderPrice < $data[$i+1][ "price_range" ] )
return $data[$i][ "percent_discount" ];
}
if ( $data[ count($data)-1 ][ "price_range" ] < $orderPrice )
return $data[ count($data)-1 ][ "percent_discount" ];
}

return 0;
}


Если сумма заказа совпадает с суммой, от которой начинается новая скидка, то скидка будет ноль :).
Т.е. если до 5000руб скидка 4%, а от 5000руб - 5%, то на заказ 4999руб будет скидка 4%, на 5001руб - 5%, а вот на 5000руб - 0% :))).

В общем, зачем там такой развесистый код я не понял и заменил на:


function _calculateGeneralPriceDiscount( $orderPrice, $log )
{
$data = db_query("SELECT price_range, percent_discount FROM ".ORDER_PRICE_DISCOUNT_TABLE." ORDER BY price_range");
$discount = 0;
while( $row = db_fetch_row($data) ) if ($row['price_range'] <= $orderPrice) $discount = $row['percent_discount'];
return $discount;
}


===========================

Нашел не ошибку, но весьма странный стиль программирования.
файл cart_functions.php, функция cartGetCartContent()


$costUC = GetPriceProductWithOption( $variants, $shopping_cart_item["productID"] );
$tmp = array(
"productID" => $product["productID"],
...
"costUC" => $costUC,
"cost" => show_price($cart_item["Quantity"]*GetPriceProductWithOption($variants,$shopping_cart_item["productID"])),
...


Зачем вычислять второй раз GetPriceProductWithOption($variants,$shopping_cart_item["productID"]), когда ровно это же число находится в переменной $costUC строкой выше? Оно ведь нифига не бесплатно:


function GetPriceProductWithOption($variants, $productID)
{
$q=db_query("select Price from ".PRODUCTS_TABLE." where productID=".(int)$productID);
$r=db_fetch_row($q);
$base_price = (float)$r[0];
$full_price = (float)$base_price;
foreach($variants as $vars)
{
$q1=db_query("select price_surplus from ".PRODUCTS_OPTIONS_SET_TABLE.
" where productID=".(int)$productID." AND variantID=".(int)$vars);
$r1=db_fetch_row($q1);
$full_price += $r1["price_surplus"];
}
return $full_price;
}


т.е. при десяти возможных вариантах получаем лишних 11 SQL-запросов.

А если посмотреть, то и вообще всю функцию GetPriceProductWithOption можно заменить на "двухзапросную". Первым запросом получаем базовую цену, вторым - изменение от базовой для переданного набора вариантов.


function GetPriceProductWithOption($variants, $productID)
{
$row=db_fetch_assoc(db_query("SELECT Price FROM ".PRODUCTS_TABLE." WHERE productID=$productID LIMIT 1"));
if (!$variants) return $row['Price'];
$row1=db_fetch_assoc(db_query("SELECT SUM(price_surplus) AS price_surplus FROM ".PRODUCTS_OPTIONS_SET_TABLE." WHERE productID=$productID AND variantID IN (".implode(',',$variants).")"));
return $row['Price']+$row1['price_surplus'];
}




=================================

На этот раз ошибка ни на что не влияющая, но прикольная :).
файл product_functions.php
функция GetExtraParametrs
в SQL-запросе чудный текст (два раза повторяется одно условие):


WHERE povvt.optionID='.$_Row['optionID'].' AND post.productID='.$_Row['productID'].' AND povvt.optionID='.$_Row['optionID']


==================================

Ошибка: штатный и аж четыре раза встречающийся в index.php код совершенно бессмысленен:


if ($smarty -> get_template_vars("main_content_template") == ("category.tpl.html" || "category_search_result.tpl.html"))


Это кусок условия вывода блоков. Левого, правого, верхнего, нижнего. Четыре одинаковых куска кода.

Правильно так:


if ($smarty -> get_template_vars("main_content_template") == "category.tpl.html" || get_template_vars("main_content_template") == "category_search_result.tpl.html")


==================================

Не ошибка. Просто заменяем многострочное извращение с ob_start() на var_export(), появившийся в PHP5
Снимает проблему "белого экрана" в некоторых редких случаях.


function db_query($s) //database query

[...]

/*
ob_start();
var_dump($_GET);
var_dump($_POST);
$tmpa=ob_get_contents();
ob_end_clean();
$out .= $tmpa;
*/
$out .= var_export($_REQUEST,true);


====================================

Аналогично.


function error_reporting_log($error_num, $error_var, $error_file, $error_line) {

[...]

/*
ob_start();
var_dump($_GET);
var_dump($_POST);
$tmpa = ob_get_contents();
ob_end_clean();
$out .= $tmpa;
*/
$out .= var_export($_REQUEST,true);


========================================

Если разрешено задавать количество товара при добавлении в корзину (опция "Разрешить указывать количество товара для добавления в корзину"), а у товара задана не единица в минимальном заказе (скажем, купить можно начиная от N штук), то при задании в окошке выбранного количества числа M получим в корзине не М товаров, а M+N-1. Происходит это потому, что товар добавляется поштучно в цикле от 1 до M (см. shopping_cart.php)


for ($mcn=0; $mcn<$_GET["multyaddcount"]; $mcn++) cartAddToCart( $_GET["add2cart"], $variants );


но при каждом "поштучном" добавлении происходит проверка корзины и если товара там еще нет (первое добавление), то добавляется не 1, а N (см. cart_functions.php, cartAddToCart)


$count_to_order = 1;
....
if ( $item_index == -1 ) $count_to_order = $min_order_amount;
....
db_query("UPDATE ".SHOPPING_CARTS_TABLE." SET Quantity=".(int)$count_to_order....


Решение:

1. в файле cart_functions.php в функции cartAddToCart заменяем


return true;


на


return $count_to_order;


все равно нигде (ни в одном месте из двух :)) не проверяется, что вернула эта функция.

2. в файле cart.php находим строку cartAddToCart

и заменяем


for ( $mcn = 0; $mcn < $_GET["multyaddcount"]; $mcn++ )
cartAddToCart(( int ) $_GET["addproduct"], $variants);


на


for ( $mcn = 0; $mcn < $_GET["multyaddcount"]; $mcn+=$added )
if (!$added = cartAddToCart(( int ) $_GET["addproduct"], $variants)) break;


3. в файле shopping_cart.php находим строку cartAddToCart

и заменяем


for ($mcn=0; $mcn<$_GET["multyaddcount"]; $mcn++) cartAddToCart( $_GET["add2cart"], $variants );


на


for ($mcn=0; $mcn<$_GET["multyaddcount"]; $mcn+=$added) if (!$added = cartAddToCart( $_GET["add2cart"], $variants )) break;


============================================

В модулях курьерской доставки (файлы class.courier2.php и class.courier.php) SQL-таблица SS__courier_rates не имеет префикса, т.е. при двух и более сайтах в одной SQL-базе могут быть проблемы,
т.к. при удалении этого модуля доставки таблица чистится либо удаляется.

============================================

Не ошибка, просто упрощаем код:

1. в файле cart.php

заменяем


if ( isset ( $_SESSION["log"] ))
//taking products from database
{
$q = db_query("select itemID, Quantity FROM ".SHOPPING_CARTS_TABLE." WHERE customerID=".( int ) regGetIdByLogin($_SESSION["log"]));
while ( $row = db_fetch_row($q)) {
$q1 = db_query("select productID from ".SHOPPING_CART_ITEMS_TABLE." where itemID=".( int ) $row["itemID"]);
$r1 = db_fetch_row($q1);
$variants = GetConfigurationByItemId($row["itemID"]);
$k += GetPriceProductWithOption($variants, $r1["productID"]) * $row["Quantity"];
$cnt += $row["Quantity"];
}
}
else
if ( isset ( $_SESSION["gids"] ))
//...session vars
{
for ( $i = 0; $i < count($_SESSION["gids"]); $i++ ) {
if ( $_SESSION["gids"][$i] ) {
$t = db_query("select Price FROM ".PRODUCTS_TABLE." WHERE productID=".( int ) $_SESSION["gids"][$i]);
$rr = db_fetch_row($t);
$sum = $rr["Price"];
// $rr["Price"]
foreach ( $_SESSION["configurations"][$i] as $varconf ) {
$q1 = db_query("select price_surplus from ".PRODUCTS_OPTIONS_SET_TABLE." where variantID=".( int ) $varconf." AND productID=".( int ) $_SESSION["gids"][$i]);
$r1 = db_fetch_row($q1);
$sum += $r1["price_surplus"];
}
$k += $_SESSION["counts"][$i] * $sum;
$cnt += $_SESSION["counts"][$i];
}
}
}


на


$k = $resCart["total_price"];
#$k = $resCart["total_price"] - $resDiscount["discount_standart_unit"]; // если хотим видеть в корзине сумму сразу со скидкой
foreach ($resCart["cart_content"] as $rc) $cnt += $rc["quantity"];



2. в файле shopping_cart_info.php

заменяем


if (isset($_SESSION["log"])) //taking products from database
{
$q = db_query("select itemID, Quantity FROM ".SHOPPING_CARTS_TABLE.
" WHERE customerID=".regGetIdByLogin($_SESSION["log"]));
while ($row = db_fetch_row($q))
{
$q1=db_query("select productID from ".SHOPPING_CART_ITEMS_TABLE.
" where itemID=".$row["itemID"]);
$r1=db_fetch_row($q1);
if($r1["productID"]){
$variants=GetConfigurationByItemId( $row["itemID"] );
$k += GetPriceProductWithOption($variants, $r1["productID"])*$row["Quantity"];
$cnt+=$row["Quantity"];
}
}
}
else
if (isset($_SESSION["gids"])) //...session vars
{
for ($i=0; $i<count($_SESSION["gids"]); $i++)
{
if ($_SESSION["gids"][$i])
{
$t = db_query("select Price FROM ".PRODUCTS_TABLE." WHERE productID=".(int)$_SESSION["gids"][$i]);
$rr = db_fetch_row($t);

$sum=$rr["Price"];

// $rr["Price"]
foreach( $_SESSION["configurations"][$i] as $vars )
{
$q1=db_query("select price_surplus from ".PRODUCTS_OPTIONS_SET_TABLE.
" where variantID=".(int)$vars." AND productID=".(int)$_SESSION["gids"][$i]);
$r1=db_fetch_row($q1);
$sum+=$r1["price_surplus"];
}

$k += $_SESSION["counts"][$i]*$sum;
$cnt += $_SESSION["counts"][$i];
}
}
}


на


$resCart = cartGetCartContent();
$k = $resCart["total_price"];
#$resDiscount = dscCalculateDiscount($resCart["total_price"], isset ( $_SESSION["log"] ) ? $_SESSION["log"] : ""); // если хотим видеть в корзине сумму сразу со скидкой
#$k = $resCart["total_price"] - $resDiscount["discount_standart_unit"];
foreach ($resCart["cart_content"] as $rc) $cnt += $rc["quantity"];


=============================================

Не ошибка, хотя как посмотреть. Дополнение резко сокращает количество SQL-запросов при сохранении настроек товаров и категорий. Актуально для сайтов с большим количеством категорий. Например, штатно на тысяче категорий будет 16тысяч (!!!) SQL-запросов. Здесь, насколько я помню, четыре запроса не зависимо от количества категорий.


Установка:

В файле category_functions.php

1. в функции update_psCount

строку


update_pCount($parent);


заменяем на


update_pCount_new($parent);


2. куда-нибудь рядом добавляем две функции


function update_sCount_new($parent, $counts = array(1 => array ('subcats' => 0,'admin_count' => 0,'customer_count' => 0)))
{
global $mc;
foreach (array_keys ($mc,(int)$parent) as $val)
{
$counts[$val]['subcats'] = count(array_keys($mc, $val));
$counts[$val]['admin_count'] = 0;
$counts[$val]['customer_count'] = 0;
if ($counts[$val]['subcats']) $counts = update_sCount_new($val,$counts);
}
return $counts;
}

function update_pCount_new($parent)
{
global $fc;
$qstr = array(
"SELECT categoryID, COUNT(*) AS admcount, COUNT(NULLIF(enabled,0)) AS count FROM ".PRODUCTS_TABLE." GROUP BY categoryID",
"SELECT pc.categoryID, COUNT(*) AS admcount, COUNT(NULLIF(enabled,0)) AS count FROM ".CATEGORIY_PRODUCT_TABLE." AS pc JOIN ".PRODUCTS_TABLE." USING (productID) GROUP BY pc.categoryID");
$counts = update_sCount_new($parent);
foreach ($qstr as $str)
{
$data = db_query($str);
while ($row=db_fetch_assoc($data))
{
$categoryID = (int)$row['categoryID'];
while ($categoryID)
{
$counts[$categoryID]['admin_count'] += (int)$row['admcount'];
$counts[$categoryID]['customer_count'] += (int)$row['count'];
$categoryID = $fc[$categoryID]['parent'];
}
}
}
$data = db_query("SELECT categoryID, subcount, products_count, products_count_admin FROM ".CATEGORIES_TABLE);
while ($row = db_fetch_assoc($data))
{
$categoryID = (int)$row['categoryID'];
if ((int)$row['subcount'] != $counts[$categoryID]['subcats'] || (int)$row['products_count'] != $counts[$categoryID]['customer_count'] || (int)$row['products_count_admin'] != $counts[$categoryID]['admin_count'])
db_query("UPDATE ".CATEGORIES_TABLE." SET subcount=".$counts[$categoryID]['subcats'].", products_count=".$counts[$categoryID]['customer_count'].", products_count_admin=".$counts[$categoryID]['admin_count']." WHERE categoryID=$categoryID");
}
}

  • 6


#30142 Еще один фильтр. Теперь на AJAX.

Написано badisoft на 09 March 2014 - 06:01 PM

Новый фильтр. Сделан с использованием AJAX, т.е. не перезагружает страницу для вывода результатов работы фильтра.


Традиционное спасибо всем, кто помогал мне (явно или путем сообщения об ошибках) в предыдущем фильтре. Без него не было бы этого.Советы, рекомендации, пожелания, сообщения об ошибках продолжают приветствоваться.


Краткое описание:
-----------------

- Как и предыдущий этот фильтр не требует в каждой категории задавать список разрешенных к показу характеристик, а строит этот список сам суммируя характеристики всех товаров категории.

- Как и предыдущий этот фильтр работает ТОЛЬКО с характеристиками товаров, заданными в виде вариантов характеристик. Т.е. с характеристиками в виде текстовой строки этот фильтр НЕ РАБОТАЕТ.

- При установке-снятии галки на лету меняются количественные показатели у остальных вариантов выбора.
Варианты с "нулевым" количеством товаров дизаблятся.

- При перемещении движка цен аналогично, но "нулевые" варианты не дизаблятся, а убираются.

- выборка товаров (показ результата работы фильтра) делается сразу по нажатию любой галки.Точнее, по изменению любого элемента формы, т.е. галки чекбокса, движков, вида сортировки, количества товаров на странице. Но это отключаемо и изначально отключено (галка "Автопоказ").

- Я не стал (пока?) делать в этом фильтре изыски предыдущего (тип выбора - чекбосы, строка, селект, движки ).
По фактам установки предыдущего фильтра у меня сложилось уверенное впечатление, что никто этими возможностями не пользуется кроме заказчика возможности :). А все остальные просто оставляют дефолтовые чекбоксы, это самое удобное.

По сути, преимуществ перед штатным у этого фильтра всего два - выбор более одного варианта для характеристики и получение результата тут же, без нажатия кнопки "Показать" и перезагрузки страницы.

Ну а задание показа/непоказа характеристик в кратком/полном описаниях, бывшее в предыдущем фильтре и вообще к фильтру не относится, есть отдельный модуль на эту тему.


Установка проста до безобразия:
-------------------------------

1. копируем файлы (они не совпадают по имени со штатными, можно смело копировать).


filter.php -> core/includes/processor/
filter.tpl.html -> core/tpl/user/[шаблон]/
filter_new.tpl.html -> core/tpl/user/[шаблон]/blocks
jquery.blockui.js -> data/[шаблон]/
jquery.form.js -> data/[шаблон]/
loading.gif -> data/[шаблон]/


2. В head.tpl.html вписываем линки на jquery-ui и css от него. Ну и на сам jquery, если он еще не вписан


<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>



3. в файле index.php

строку


if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file" ))) {


заменяем на


# BEGIN filter-ajax
#if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file" ))) {
if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file", "filter" ))) {
# END filter-ajax



4. в файле index.tpl.html задаем, куда и как выводить результат работы фильтра,
замещающий все содержимое блока с id="filterResult".

Например, если строки


{* вывод центральной страницы *}
{include file="$main_content_template"}


заменить на


<div id="filterResult">

{* вывод центральной страницы *}
{include file="$main_content_template"}

</div>


то список отфильтрованного выведется вместо основного содержимого сайта, но верхние и нижние блоки останутся.
А если заменить


{* верхние блоки *}
{include file="blocks.tpl.html" binfo=$top_blocks bclass="hdbtop" balign="left"}
{* вывод центральной страницы *}
{include file="$main_content_template"}
{* нижние блоки *}
{include file="blocks.tpl.html" binfo=$bottom_blocks bclass="hdbtop" balign="left"}


на


<div id="filterResult">

{* верхние блоки *}
{include file="blocks.tpl.html" binfo=$top_blocks bclass="hdbtop" balign="left"}
{* вывод центральной страницы *}
{include file="$main_content_template"}
{* нижние блоки *}
{include file="blocks.tpl.html" binfo=$bottom_blocks bclass="hdbtop" balign="left"}

</div>


то список отфильтрованного заместит все центральное содержимое.


5. Создаем в "Управлении блоками" через "Добавить блок из файла" новый блок из файла filter_new.tpl.html


Посмотреть, как это работает можно на http://cpu.badisoft.ru - слева ниже большой красной кнопки "мобильная версия" есть блок "Новый фильтр на Аяксе" и в нем кнопка "Развернуть".

Прикрепленные файлы

  • Прикрепленный файл  filter-ajax.zip   32.19К   170 Количество загрузок:

  • 6


#29943 Другой шаблон при заходе с мобильного

Написано badisoft на 12 February 2014 - 08:55 PM

Дополнение позволяет сделать так, чтобы для мобильных устройств и обычных грузились разные шаблоны.
Расположение мобильного шаблона штатное: tpl.html-файлы в core/tpl/user/[имя шаблона], а остальной набор данных (js, css, картинки) - при необходимости - в data/[имя шаблона].

Спонсор дополнения: kery

Установка:
=========

1. копируем файлы

addon.php -> core/includes/admin/
is_mobile.php -> core/functions/


2. создаем каталог core/cache/mobile/


3. в файле index.php

3.1 после строки


$smarty = new Smarty;


вставляем


# BEGIN Mobile-template
if (isset($_GET['mobile_template'])) $_SESSION['mobile_template'] = ($_GET['mobile_template'] == 'yes');
if (!isset($_SESSION['mobile_template'])) $_SESSION['mobile_template'] = is_mobile();
if ($_SESSION['mobile_template'])
{
$smarty->compile_dir = 'core/cache/mobile';
$smarty->cache_dir = 'core/cache/mobile';
}
# END Mobile-template


3.2 вместо строки


if (isset($_SESSION["CUSTOM_DESIGN"]))


вставляем


# BEGIN Mobile-template
# if (isset($_SESSION["CUSTOM_DESIGN"]))
if ($_SESSION['mobile_template'])
{
$smarty->template_dir = "core/tpl/user/".CONF_MOBILE_TEMPLATE;
define('TPL', CONF_MOBILE_TEMPLATE);
}
elseif (isset($_SESSION["CUSTOM_DESIGN"]))
# BEGIN Mobile-template


4. Заходим в админку. Это нужно, чтобы чтобы отработал файл addon.php и создалась настройка выбора "мобильного" шаблона.
В админке в "Общих настройках" появится возможность задать еще один шаблон - для показа мобильным устройствам.


Принудительная смена шаблона происходит путем GET-запроса mobile_template=yes или mobile_template=no
Например, путем создания ссылки <a href="/?mobile_template=no">Обычная версия сайта</a> в мобильной версии.

В комплекте идут три совершенно не отлаженных мобильных минимум-миниморум шаблончика на jquery-mobile, найденные в инете. Они не имеют ни малейшего отношения к товарному наполнению и тематике сайта и добавлены просто для понимания, как все это работает.

Прикрепленные файлы


  • 6


#22915 Пробел в окне редактора Fсkeditor

Написано 64j на 10 October 2012 - 04:03 PM

При создании товара или категории, в поле визуального редактора присутствует неразрывный пробел.
В коде показывает
&nbsp;
Всё бы хорошо, но если делать проверку на существование описания товара или категории, то описание не является пустым. То есть и описания нету, a и вроде как есть.
Обрыл кучу форумов с обсуждениями данного редактора, но советы , что были - не помогли.


Нашёл в чём причина.
в двух файлах
fckeditor/editor/js/fckeditorcode_gecko.js
fckeditor/editor/js/fckeditorcode_ie.js

найти в коде на 48 - 49 строке
A=E[1]+'&nbsp;'+F[1];
и заменить на
A=E[1]+''+F[1];

  • 6


#19545 Красивое двухуровневое меню категорий на JQ Lava lamp + Superfish

Написано drserg на 12 May 2012 - 05:59 PM

Здравствуйте, только начал создавать на Shop CMS'е. Хочу с вами поделится своим блоком category_tree. Для данного меню я использовал 2 JQ плагина: Lava lamp (первый уровень) + Superfish (2ой уровень). В архиве демо меню, и сам файл блока. Надеюсь Вам все будет понятно, и вы знаете как добавлять скрипты и стили и включать плагины )). По любым вопросам обращайтесь.

Добавил скриншот (но JQ скриншотом не опишешь).
Изображение

Прикрепленные файлы

  • Прикрепленный файл  lava_menu.rar   88.82К   67 Количество загрузок:

  • 6


#34408 Поиск заказа по дополнительному полю

Написано badisoft на 10 March 2016 - 02:48 PM

Дополнение позволяет искать заказы по содержимому дополнительных полей (см. раздел "Форма регистрации" в админке).
Писалось под конкретное дополнительное поле "Телефон", т.к. у заказчика оно было одно, но подходит для поиска по любому полю - индекс, девичья фамилия матери и т.п.

при одном доп.поле:
Прикрепленный файл  1.jpg   12.41К   4 Количество загрузок:

при нескольких:
Прикрепленный файл  2.jpg   13.21К   4 Количество загрузок:

Установка:
==========

1. в файле order_functions.php в функции ordGetOrders

перед


if ( isset($callBackParam["orderStatuses"]) )


вставляем


# BEGIN поиск по дополнительному полю
if ( isset($callBackParam['reg_field_id']) && isset($callBackParam['reg_field_value']))
{
#$where = " WHERE reg_field_ID=".$callBackParam['reg_field_id']." AND REPLACE(REPLACE(reg_field_value,'-',''),' ','') LIKE '%".$callBackParam['reg_field_value']."%'";
$where = " WHERE reg_field_ID=".$callBackParam['reg_field_id']." AND reg_field_value LIKE '%".$callBackParam['reg_field_value']."%'";
$select_registered = "SELECT customerID FROM ".CUSTOMER_REG_FIELDS_VALUES_TABLE.$where;
$select_quick = "SELECT orderID FROM ".CUSTOMER_REG_FIELDS_VALUES_TABLE_QUICKREG.$where;
$where_clause .= " customerID IN ($select_registered) OR orderID IN ($select_quick) AND customerID=0";
}
# END поиск по дополнительному полю



2. в файле custord_new_orders.php

2.1. в функции _setCallBackParamsToSearchOrders

в самый конец вставляем


# BEGIN поиск по дополнительному полю
else if ( $_GET["order_search_type"] == "reg_field" )
{
$callBackParam["reg_field_id"] = (int)$_GET["reg_field_id"];
$callBackParam["reg_field_value"] = trim($_GET["reg_field_value"]);
}
# END поиск по дополнительному полю


2.2. в функциях _getReturnUrl, _getUrlToNavigate и _getUrlToSort

после


$url .= "&orderID_textbox=".$_GET["orderID_textbox"];


вставляем


# BEGIN поиск по дополнительному полю
if (isset($_GET["reg_field_id"])) $url .= "&reg_field_id=".$_GET["reg_field_id"];
if (isset($_GET["reg_field_value"])) $url .= "&reg_field_value=".$_GET["reg_field_value"];
# END поиск по дополнительному полю



3. в файле menu.tpl.html

3.1. перед


<tr>
<td align="left"><input type=radio class="round" name=order_search_type id=order_search_type2 value='SearchByStatusID'


вставляем


{* BEGIN поиск по дополнительному полю *}
{assign var='reg_fields' value=0|GetRegFields}
{if $reg_fields|@count}
<tr>
<td align="left" width="16" valign="middle">
<input type=radio class="round" name=order_search_type id=order_search_type3 value='reg_field'{if $order_search_type == 'reg_field'} checked{/if} onclick='order_search_typeClickHandler()'>
</td>
<td valign="middle" height="20">Поиск по доп.полю</td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="left">
{if $reg_fields|@count==1}
<input type='hidden' name='reg_field_id' id='reg_field_id' value='{$reg_fields[0].reg_field_ID}'>{$reg_fields[0].reg_field_name}<br>
{else}
<select name='reg_field_id' id='reg_field_id' style='width:126px;'>
{foreach from=$reg_fields item=reg_field}
<option value="{$reg_field.reg_field_ID}"{if $reg_field.reg_field_ID==$smarty.get.reg_field_ID} selected{/if}>{$reg_field.reg_field_name}</option>
{/foreach}
</select>
{/if}
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td align="left"><input type=text name='reg_field_value' id='reg_field_value' value='{$smarty.get.reg_field_value}' class="prc" size="21"></td>
</tr>
<tr><td height="6" colspan="2"></td></tr>
{/if}
{* END поиск по дополнительному полю *}


3.2. после


document.getElementById('id_checkall').disabled = true;


вставляем


// BEGIN поиск по дополнительному полю
document.getElementById('reg_field_id').disabled = true;
document.getElementById('reg_field_value').disabled = true;
// END поиск по дополнительному полю


3.3. после


document.getElementById('id_checkall').disabled = false;


вставляем


// BEGIN поиск по дополнительному полю
document.getElementById('reg_field_id').disabled = true;
document.getElementById('reg_field_value').disabled = true;
// END поиск по дополнительному полю


3.4. перед


{literal}}{/literal}
order_search_typeClickHandler();


вставляем


// BEGIN поиск по дополнительному полю
else if ( document.getElementById('order_search_type3').checked )
{literal}{{/literal}
{section name=i loop=$order_statuses}
document.getElementById('checkbox_order_status_{$order_statuses[i].statusID}').disabled = true;
{/section}
document.getElementById('id_checkall').disabled = true;
document.getElementById('orderID_textbox').disabled = true;
document.getElementById('reg_field_id').disabled = false;
document.getElementById('reg_field_value').disabled = false;
{literal}}{/literal}
// END поиск по дополнительному полю

  • 5


#27572 Картинки для вариантов характеристик

Написано badisoft на 24 July 2013 - 05:33 PM

По необходимости (как часть другого модуля) понадобился этот модуль. По мере изучения модуля namer-а от него остались практически рожки да ножки, но исходным прообразом был таки он, т.е. писалось не с нуля.
Основные отличия от модуля namer-а:
- картинки можно присваивать не только новым создаваемым вариантам, но и любому уже имеющемуся
- файлы картинок удаляются при удалении варианта
- файл картинки сохраняет исходное имя если такого файла нет либо получает имя *_1.*, *_2.*, *_3.* и так далее, т.е. первое по счету не занятое.

Где-то получилось сложнее, чем в модуле namer-а, где-то проще. Изменения в коде product_brief.tpl.html и product_detailed.tpl.html (пункты 7 и 8) родные namer-овские, ничего не менял, т.к. "другой модуль" этих файлы не затрагивает.

Спонсор создания инструкции - l17l

Что не понятно - спрашивайте. Но не в личке, а тут.

Админка выглядит примерно так:
Прикрепленный файл  1.JPG   48.1К   167 Количество загрузок:

================================================
Инструкция по установке модуля картинок для вариантов характеристик и вывода их (картинок) в шаблонах краткого и полного описания.

1. в файле option_functions.php

1.1 в функции optGetOptionValues строку


$q = db_query("select variantID, optionID, option_value, sort_order from ".


заменяем на


# BEGIN variants-with-pictures
#$q = db_query("select variantID, optionID, option_value, sort_order from ".
$q = db_query("select variantID, optionID, option_value, sort_order, picture from ".
# END variants-with-pictures


1.2 в конец файла добавляем функцию


# BEGIN variants-with-pictures
function optaddoptionvalue_by_namer( $optionID, $value, $sort_order, $picture )
{
db_query( "insert into ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." (optionID, option_value, sort_order, picture) values('".$optionID."', '".xtotext($value)."', '".$sort_order."', '".xtotext($picture)."')");
return db_insert_id();
}
# END variants-with-pictures



2. в файле product_functions.php

2.1 в функции GetExtraParametrs

строку


$sql = 'select povvt.option_value, povvt.variantID, post.price_surplus


заменяем на


# BEGIN variants-with-pictures
#$sql = 'select povvt.option_value, povvt.variantID, post.price_surplus
$sql = 'select povvt.option_value, povvt.variantID, povvt.picture, post.price_surplus
# END variants-with-pictures


после строки


$_Row['values_to_select'][$i]['option_value'] = $_Rowue['option_value'];


вставляем


# BEGIN variants-with-pictures
$_Row['values_to_select'][$i]['picture'] = $_Rowue['picture'];
# END variants-with-pictures



3. в файле catalog_extra.php

3.1 перед строкой


if (isset($_GET["save_successful"])) //update was successful


вставляем


# BEGIN variants-with-pictures
if (!db_fetch_row(db_query("SHOW COLUMNS FROM ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." LIKE 'picture'"))) db_query("ALTER TABLE ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." ADD picture varchar(50) NULL");
if(!file_exists("data/option_image")) mkdir("data/option_image");
# END variants-with-pictures


3.2 перед строкой


// update existing values


вставляем


# BEGIN variants-with-pictures
$updatePictures = ScanFilesVariableWithId(array( "add_picture"));
foreach( $updatePictures as $key => $val )
{
if ( isset($val["add_picture"]) )
{
$picture = saveVariantsPicture('add_picture_'.$key);
if ($picture)
{
$row = db_fetch_assoc(db_query("SELECT picture from ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." WHERE variantID=$key"));
if ($picture != $row['picture'])
{
if (!empty($row['picture']) && file_exists("data/option_image/".$row['picture'])) unlink("data/option_image/".$row['picture']);
db_query("UPDATE ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." SET picture='$picture' WHERE variantID=$key");
}
}
}
}
# END variants-with-pictures



3.3 строки


optAddOptionValue($_POST["optionID"], $_POST["add_value"],
(int)$_POST["add_sort"] );


заменяем на


# BEGIN variants-with-pictures
#optAddOptionValue($_POST["optionID"], $_POST["add_value"],
#(int)$_POST["add_sort"] );
optAddOptionValue_by_namer($_POST["optionID"], $_POST["add_value"], (int)$_POST["add_sort"], saveVariantsPicture('add_option_picture'));
# END variants-with-pictures


3.4 после строки


$variantid = (int)$_GET["kill_value"];


вставляем


# BEGIN variants-with-pictures
$row = db_fetch_assoc(db_query("SELECT picture from ".PRODUCTS_OPTIONS_VALUES_VARIANTS_TABLE." WHERE variantID=".$variantid));
if (!empty($row['picture']) && file_exists("data/option_image/".$row['picture'])) unlink("data/option_image/".$row['picture']);
# END variants-with-pictures



3.5 в конец файла вставляем


# BEGIN variants-with-pictures
function saveVariantsPicture($add_picture)
{
if($_FILES[$add_picture]['tmp_name'])
{
if(!is_uploaded_file($_FILES[$add_picture]['tmp_name'])) die("Проблема загрузки файла");
if(substr($_FILES[$add_picture]['type'],0,5) != 'image') die ("Разрешена загрузка только изображений");
if (filesize($_FILES[$add_picture]['tmp_name']) > 20000) die ("Размер файла превышает допустимый (20 КБ)");
$picture = $_FILES[$add_picture]['name'];
$expl = explode(".",$picture,2);
$i=1;
while (file_exists("data/option_image/".$picture)) $picture = $expl[0]."_".$i++.".".$expl[1];
if (!move_uploaded_file($_FILES[$add_picture]['tmp_name'],"data/option_image/".$picture)) die("Не удалось скопировать файл из временного каталога сервера в каталог data/option_image");
return $picture;
}
else return NULL;
}
# END variants-with-pictures



4. в файле catalog_extra.tpl.html

4.1 вместо строки


<form action="{$smarty.const.ADMIN_FILE}" method=POST name="formext" id="formext">


вставляем


{* BEGIN variants-with-pictures *}
{*<form action="{$smarty.const.ADMIN_FILE}" method=POST name="formext" id="formext">*}
<form action="{$smarty.const.ADMIN_FILE}" method=POST name="formext" id="formext" enctype="multipart/form-data">
{* END variants-with-pictures *}


4.3 перед строкой


<td align="left" class="toph3" width="100%">{$smarty.const.ADMIN_ONE_VALUE}</td>


вставляем


{* BEGIN variants-with-pictures *}
<td align="left" class="toph3">Картинка</td>
{* END variants-with-pictures *}


4.4 строку


<td align="left"><input name="option_value_{$values[i].variantID}" type=text value="{$values[i].option_value}" class="textp" size="30"></td>


заменяем на


{* BEGIN variants-with-pictures *}
{*<td align="left"><input name="option_value_{$values[i].variantID}" type=text value="{$values[i].option_value}" class="textp" size="30"></td>*}
<td align="left" valign="middle">
{if $values[i].picture}
<img src="data/option_image/{$values[i].picture}" alt="" width="50" style="vertical-align:middle;">&nbsp;&nbsp;
{else}
<img src="data/admin/pixel.gif" alt="" width="50" height="1">&nbsp;&nbsp;
{/if}
</td>
<td align="left">
<input name="option_value_{$values[i].variantID}" type=text value="{$values[i].option_value}" class="textp" size="70">
<input type=file class="file" name="add_picture_{$values[i].variantID}" value="" size="26">
</td>
{* END variants-with-pictures *}


4.5 строки


<td align="left" width="100%">{$smarty.const.ADMIN_SORT_ORDER}</td></tr>
<tr class="lins">
<td align="left"><input name="add_value" type=text value="" class="textp" size="46"></td>


заменяем на


{* BEGIN variants-with-pictures *}
{*
<td align="left" width="100%">{$smarty.const.ADMIN_SORT_ORDER}</td></tr>
<tr class="lins">
<td align="left"><input name="add_value" type=text value="" class="textp" size="46"></td>
*}
<td align="left" width="20%">{$smarty.const.ADMIN_SORT_ORDER}</td></tr>
<tr class="lins">
<td align="left">
<input type=text class="textp" name="add_value" value="" size="70">
<input type=file class="file" name="add_option_picture" value="" size="26">
</td>
{* END variants-with-pictures *}



5. Заходим в админке в "характристики товаров". При этом создадутся новые необходимые поля в таблицах.


7. в файле product_brief.tpl.html

7.1 ниже строки


{if $smarty.section.k.index == 0}


находится шаблон select-а примерно такого вида (пустые строки и пробелы удалены):


<select name='option_select_{$select_counter_var}_{$product_info.productID}' onchange='GetCurrentCurrency_{$product_info.productID}();' class="WCHhider">
{section name=j loop=$product_info.product_extra[i].values_to_select}
<option value='{$product_info.product_extra[i].values_to_select[j].price_surplus}:{$product_info.product_extra[i].values_to_select[j].variantID}' {if $product_info.product_extra[i].values_to_select[j].variantID eq $product_info.product_extra[i].variantID}selected{/if}>{$product_info.product_extra[i].values_to_select[j].option_value}</option>
{/section}
</select>


его надо заменить на


{section name=j loop=$product_info.product_extra[i].values_to_select}
<br>
{if $product_info.product_extra[i].values_to_select[j].picture ne NULL}
<img src="data/option_image/{$product_info.product_extra[i].values_to_select[j].picture}" alt="" width="50" style="vertical-align:middle;">
{/if}
<input type="radio" onclick="GetCurrentCurrency_{$product_info.productID}({$product_info.product_extra[i].values_to_select[j].price_surplus}, {$product_info.product_extra[i].values_to_select[j].variantID});" name='option_select_{$select_counter_var}_{$product_info.productID}' value='{$product_info.product_extra[i].values_to_select[j].price_surplus}:{$product_info.product_extra[i].values_to_select[j].variantID}' {if $product_info.product_extra[i].values_to_select[j].variantID eq $product_info.product_extra[i].variantID}checked{/if}>
{$product_info.product_extra[i].values_to_select[j].option_value}
<br>
{/section}


7.2 в JS-функции GetCurrentCurrency_{$product_info.productID}()

строку


_value=document.MainForm1_{$product_info.productID}.option_select_{$select_counter_var2}_{$product_info.productID}.value;


заменяем на


_value = "";
for (var i=0; i < document.MainForm1_{$product_info.productID}.option_select_{$select_counter_var2}_{$product_info.productID}.length; i++)
if (document.MainForm1_{$product_info.productID}.option_select_{$select_counter_var2}_{$product_info.productID}[i].checked)
_value=document.MainForm1_{$product_info.productID}.option_select_{$select_counter_var2}_{$product_info.productID}[i].value;



8. в файле product_detailed.tpl.html

8.1 ниже строки


{if $smarty.section.k.index == 0}


находится шаблон select-а примерно такого вида (пустые строки и пробелы удалены):


<select name='option_select_{$select_counter_var}' onchange='GetCurrentCurrency();' class="WCHhider">
{section name=j loop=$product_extra[i].values_to_select}
<option value='{$product_extra[i].values_to_select[j].price_surplus}:{$product_extra[i].values_to_select[j].variantID}' {if $product_extra[i].values_to_select[j].variantID eq $product_extra[i].variantID}selected{/if}>{$product_extra[i].values_to_select[j].option_value}</option>
{/section}
</select>


его надо заменить на


{section name=j loop=$product_extra[i].values_to_select}
<br>
{if $product_extra[i].values_to_select[j].picture ne NULL}
<img src="data/option_image/{$product_extra[i].values_to_select[j].picture}" alt="" width="50" style="vertical-align:middle;">
{/if}
<input type="radio" onclick="GetCurrentCurrency();" name='option_select_{$select_counter_var}' value='{$product_extra[i].values_to_select[j].price_surplus}:{$product_extra[i].values_to_select[j].variantID}' {if $product_extra[i].values_to_select[j].variantID eq $product_extra[i].variantID}checked{/if}>
{$product_extra[i].values_to_select[j].option_value}
<br>
{/section}


8.2 в JS-функции GetCurrentCurrency()

строку


_value = document.MainForm.option_select_{$select_counter_var2}.value;


заменяем на


_value = "";
for (var i=0; i < document.MainForm.option_select_{$select_counter_var2}.length; i++)
if (document.MainForm.option_select_{$select_counter_var2}[i].checked)
_value=document.MainForm.option_select_{$select_counter_var2}[i].value;

  • 5


#26053 Редактирование заказа v.2.0 by vsupport.club

Написано R.Sergey на 02 April 2013 - 02:58 AM

Редактирование заказа в админ-панели.
Модуль позволяет редактировать все данные заказа:
- e-mail,
- Имя,
- Фамилия,
- Телефон,
- Адрес,
- Способ доставки (можно вводить произвольное значение, например если у Вас нет способа доставки Самовывоз, но Вы индивидуально договорились об этом с заказчиком, можно написать способ Самовывоз),
- Способ платы (так как и в доставке можно ввести произвольное значение),
- Характеристики товаров,
- Кол-во товара,
- Стоимость товара,
- Скидку в процентах, т.е. если у Вашего заказчика не было скидки, но Вы индивидуально ее предоставили это можно внести в заказ,
- Стоимость доставки.

Есть возможность добавлять/удалять товары в заказе.
Есть возможно добавлять комментарий администратора к заказу.

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

Прикрепленный файл  edit_order.jpg   606.64К   189 Количество загрузок:


Скачать модуль "Редактирование заказа в админ. панели"
  • 5


#34996 Добавить товар в избранное

Написано zcz на 01 November 2016 - 11:16 AM

Спонсор stasikons

Модуль позволяет реализовать на сайте блок "избранное".
Работает все на ajax, для зарегистрированных заноситься в базу, для посетителей в сессию.
Сразу скажу, что код в шаблонах написан под 3.1.3 версию, но переделать, думаю, труда не составит.
Все ajax и js (jquery) написаны универсально + есть закомментированные строки под старую и новую версию шаблона

Если в шаблоне не установлен jquery, то его необходимо будет поставить!

Установка:

!ВКЛЮЧАЕМ ПРИНУДИТЕЛЬНУЮ ОЧИСТКУ КЕША В АДМИНКЕ!

Заливаем файлы в свои директории.

Открываем index.php в корне (да он должен быть открытым.)
Находим строку

if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file" ))) {

Заменяем на

if ( in_array($_GET["do"], array( "captcha", "cart", "rss", "compare", "yandex", "invoice_jur", "invoice_phys", "stat", "get_file", "favourites" ))) {

в файле core/config/tables.inc.php в самый низ перед закрывающим тегом ?> вставляем

if(!defined('FAVORITES_TABLE')) {
define('FAVORITES_TABLE', 'ПРЕФИКС_favourites');
}

Открываем файл core/functions/product_functions.php
Находим функцию prdSearchProductByTemplate

в ней, в самом низу, перед $result[] = $row; вставляем

//вставка для избранных товаров
if ( isset($_SESSION["log"]) ) {
$select = mysql_query("SELECT customerID FROM ".CUSTOMERS_TABLE." WHERE `Login` = '".$_SESSION["log"]."' ");
$rows = mysql_fetch_array($select);
$favorit = mysql_query("SELECT productsID FROM ".FAVORITES_TABLE." WHERE customerID = '".$rows[0]."'");
$r = mysql_fetch_array($favorit);
$prd = explode(",", $r[0]);
foreach ($prd as $v) {
if($v == $row['productID']) {
$row["inF"] = 1;
}
}
}
else {
$prd = explode(",", $_SESSION['f_prod']);
foreach ($prd as $v) {
if($v == $row['productID']) {
$row["inF"] = 1;
}
}
}
//END вставка для избранных товаров

открываем core/tpl/user/ВАШ_ШАБЛОН/product_detailed.tpl.html

в нужном месте вставляем кусок кода для кнопки "в избранное"

{*Добавить в избранное*}
<span id="favour_{$product_info.productID}">
{if $in_favorite eq 1}
<a href="#" onclick="doFavoriteDel('pID={$product_info.productID}'); return false" class="btn btn-default">Удалить из избранного</a>
{else}
<a href="#" onclick="doFavorite('pID={$product_info.productID}'); return false" class="btn btn-default">В избранное</a>
{/if}
</span>
{*END добавить в избранное*}

в файле core/tpl/user/ВАШ_ШАБЛОН/product_brief.tpl.html в нужном месте вставляем

{*Добавить в избранное*}
<span id="favour_{$product_info.productID}">
{if $product_info.inF eq 1}
<a href="#" onclick="doFavoriteDel('pID={$product_info.productID}'); return false" class="btn btn-default btn-xs">Удалить из избранного</a>
{else}
<a href="#" onclick="doFavorite('pID={$product_info.productID}'); return false" class="btn btn-default btn-xs">В избранное</a>
{/if}			  
</span>
{*END добавить в избранное*}

в core/tpl/user/ВАШ_ШАБЛОН/index.tpl.html перед закрывающим тегом </body> добавляем


{* добавление в избранное *}
<script src="data/{$smarty.const.TPL}/favourites.js"></script>
{* END добавление в избранное *}

в htaccess необходимо в самый низ добавить строки

#Избранные товары
RewriteRule ^favourites\.html$ index.php?favourites=yes [L]
#END избранные товары

После всех изменений переходим на страницу http://ВАШ_САЙТ/favourites.html

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

Устанавливаем блок из "управления блоками" (добавить блок из файла) или вставляем вручную в нужном месте шаблона.
Если все шаги были выполнены верно, то можно наслаждаться новым модулем.

Прикрепленный файл  Добавить в избранное.rar   7.47К   25 Количество загрузок:
Прикрепленный файл  favourites_js_3_1_2.rar   902байт   8 Количество загрузок: - JS для shopcms 3.1.1 и 3.1.2
  • 7