first commit
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
$txt_str['restrictions'] = [
|
||||
'toolbar_list' => 'Seznam omezení',
|
||||
'toolbar_add' => 'Vytvořit omezení',
|
||||
'name' => 'Popis omezení',
|
||||
|
||||
'producers' => 'Značky',
|
||||
'categories' => 'Sekce',
|
||||
'campaigns' => 'Kampaně',
|
||||
'products' => 'Produkty',
|
||||
'parameters_list' => 'Parametry',
|
||||
|
||||
'countries' => 'Země',
|
||||
'users' => 'Uživatele',
|
||||
'regions' => 'Regiony',
|
||||
'login' => 'Přihlášení',
|
||||
'price_levels' => 'Cenové hladiny',
|
||||
'users_groups' => 'Skupiny uživatelů',
|
||||
|
||||
'login.logIn' => 'Přihlášený uživatel',
|
||||
'login.logOut' => 'Nepřihlášený uživatel',
|
||||
|
||||
'domain' => 'Omezíme uživatele',
|
||||
'products_hide' => 'Skryjeme produkty',
|
||||
'products_show' => 'Odkryjeme pouze produkty',
|
||||
|
||||
'search' => 'Vyhledávání',
|
||||
'searchName' => 'Podle názvu',
|
||||
];
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
use KupShop\AdminBundle\AdminList\BaseList;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: hanz
|
||||
* Date: 3.6.14
|
||||
* Time: 12:30.
|
||||
*/
|
||||
class RestrictionsList extends BaseList
|
||||
{
|
||||
protected $tableDef = [
|
||||
'id' => 'id',
|
||||
'fields' => [
|
||||
'Název' => ['field' => 'name'],
|
||||
'Koho omezíme' => ['field' => 'domain', 'render' => 'renderDomain'],
|
||||
'Co skryjeme' => ['field' => 'object_value', 'render' => 'renderObject'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Restrictions
|
||||
*/
|
||||
private $restrictions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->restrictions = ServiceContainer::getService(Restrictions::class);
|
||||
}
|
||||
|
||||
public function renderDomain($values)
|
||||
{
|
||||
$domains = array_flip($this->restrictions->getDomainNames());
|
||||
|
||||
return translate($domains[$values['domain']], 'restrictions');
|
||||
}
|
||||
|
||||
public function renderObject($values)
|
||||
{
|
||||
$objects = array_flip($this->restrictions->getObjectNames());
|
||||
|
||||
$objects_names = [];
|
||||
$object_value = json_decode($values['object_value'], true);
|
||||
|
||||
foreach ($object_value as $object => $obj_values) {
|
||||
$objects_names[] = translate($objects[$object], 'restrictions');
|
||||
}
|
||||
|
||||
return implode(', ', $objects_names);
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('id', 'name', 'object_value', 'domain')
|
||||
->from('restrictions', 'r');
|
||||
|
||||
if ($name = getVal('name')) {
|
||||
$qb->andWhere(\KupShop\CatalogBundle\Query\Search::searchFields($name, [
|
||||
['field' => 'r.name', 'match' => 'both'],
|
||||
]));
|
||||
}
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
214
bundles/KupShop/RestrictionsBundle/Admin/restrictions.php
Normal file
214
bundles/KupShop/RestrictionsBundle/Admin/restrictions.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
$main_class = 'RestrictionsAdmin';
|
||||
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
|
||||
class RestrictionsAdmin extends Window
|
||||
{
|
||||
protected $tableName = 'restrictions';
|
||||
protected $template = 'window/restrictions.tpl';
|
||||
|
||||
/**
|
||||
* @var Restrictions
|
||||
*/
|
||||
private $restrictions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->restrictions = ServiceContainer::getService(Restrictions::class);
|
||||
}
|
||||
|
||||
public function get_vars()
|
||||
{
|
||||
$vars = parent::get_vars();
|
||||
|
||||
$ID = $this->getID();
|
||||
|
||||
$pageVars = [];
|
||||
$data = &$vars['body']['data'];
|
||||
|
||||
if (findModule(Modules::PRICE_LEVELS)) {
|
||||
$pageVars['all_price_levels'] = sqlQueryBuilder()->select('*')
|
||||
->from('price_levels')
|
||||
->orderBy('name')
|
||||
->execute()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
$pageVars['all_user_groups'] = sqlQueryBuilder()->select('*')
|
||||
->from('users_groups')
|
||||
->orderBy('name')
|
||||
->execute()->fetchAllAssociative();
|
||||
|
||||
if (!empty($ID)) {
|
||||
/*
|
||||
*
|
||||
* Domain loading
|
||||
*/
|
||||
$domains = array_flip($this->restrictions->getDomainNames());
|
||||
|
||||
$fields = sqlGetConnection()->getSchemaManager()->listTableColumns('restrictions_domains');
|
||||
$fields = join(',', array_map(function ($x) {
|
||||
return 'rdv.'.$x;
|
||||
}, array_filter(array_keys($fields), function ($x) {
|
||||
return $x != 'id' && $x != 'id_restriction';
|
||||
})));
|
||||
|
||||
$sql = sqlQuery("SELECT rdv.*, COALESCE({$fields}) AS domain_value, u.name, u.surname, u.email, u.firm
|
||||
FROM restrictions_domains AS rdv
|
||||
LEFT JOIN users AS u ON u.id = rdv.id_user
|
||||
WHERE rdv.id_restriction = :id_restriction", ['id_restriction' => $ID]);
|
||||
|
||||
$domain_name = $domains[$data['domain']].'_values';
|
||||
|
||||
$pageVars[$domain_name] = [];
|
||||
|
||||
foreach ($sql as $value) {
|
||||
$value['user'] = "{$value['firm']} {$value['name']} {$value['surname']} - {$value['email']}";
|
||||
$pageVars[$domain_name][$value['domain_value']] = $value;
|
||||
}
|
||||
/*
|
||||
* End domain loading
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Object loading
|
||||
*
|
||||
*/
|
||||
$objects_values = $data['object_value'];
|
||||
|
||||
if (!empty($objects_values)) {
|
||||
$objects_values = json_decode($objects_values, true);
|
||||
}
|
||||
|
||||
$objects = array_flip($this->restrictions->getObjectNames());
|
||||
|
||||
foreach ($objects_values as $key => $object_values) {
|
||||
if (is_array($object_values)) {
|
||||
switch ($key) {
|
||||
// category
|
||||
case 1:
|
||||
$object_values = sqlFetchAll(sqlQuery('SELECT id, name FROM sections WHERE id IN (:values)',
|
||||
['values' => array_values($object_values)], ['values' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]));
|
||||
break;
|
||||
|
||||
// producer
|
||||
case 2:
|
||||
$object_values = sqlFetchAll(sqlQuery('SELECT id, name FROM producers WHERE id IN (:values)',
|
||||
['values' => array_values($object_values)], ['values' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]));
|
||||
break;
|
||||
|
||||
// campaign
|
||||
case 3:
|
||||
global $cfg;
|
||||
$loaded_values = [];
|
||||
foreach ($object_values as &$value) {
|
||||
$tmp_value['id'] = $value;
|
||||
$tmp_value['name'] = !empty($cfg['Products']['Flags'][$value]) ? $cfg['Products']['Flags'][$value] : $value;
|
||||
$loaded_values[$value] = $tmp_value;
|
||||
}
|
||||
$object_values = $loaded_values;
|
||||
break;
|
||||
|
||||
// product
|
||||
case 4:
|
||||
$object_values = sqlFetchAll(sqlQuery('SELECT id, title AS name FROM products WHERE id IN (:values)',
|
||||
['values' => array_values($object_values)], ['values' => \Doctrine\DBAL\Connection::PARAM_INT_ARRAY]));
|
||||
break;
|
||||
// parameters_list
|
||||
case 5:
|
||||
$object_values = array_values($object_values); // preloaded in js
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$data[$objects[$key].'_values'] = $object_values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$vars['body']['data'] = array_merge($vars['body']['data'], $pageVars);
|
||||
$vars['body']['restrictions'] = $this->restrictions;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
$data = parent::getData();
|
||||
|
||||
$objects = array_flip($this->restrictions->getObjectNames());
|
||||
|
||||
if (getVal('Submit')) {
|
||||
$data['object_values'] = [];
|
||||
foreach ($objects as $key => $object) {
|
||||
if (isset($data[$object.'_values'])) {
|
||||
$data['object_values'][$key] = [];
|
||||
foreach ($data[$object.'_values'] as $objects_value) {
|
||||
$data['object_values'][$key][] = $objects_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['object_value'] = json_encode($data['object_values']);
|
||||
|
||||
$domain_name = array_flip($this->restrictions->getDomainNames())[$data['domain']] ?? '';
|
||||
$data['domain_invert'] = getVal($domain_name.'_invert') ? 'Y' : 'N';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
$SQL = parent::handleUpdate();
|
||||
|
||||
$domains = array_flip($this->restrictions->getDomainNames());
|
||||
|
||||
$data = parent::getData();
|
||||
$ID = $this->getID();
|
||||
|
||||
$this->deleteSQL('restrictions_domains', ['id_restriction' => $ID]);
|
||||
|
||||
if (!empty($data[$domains[$data['domain']].'_values'])) {
|
||||
foreach ($data[$domains[$data['domain']].'_values'] as $value) {
|
||||
switch ($data['domain']) {
|
||||
// country
|
||||
case 1:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'country' => $value]);
|
||||
break;
|
||||
|
||||
// user
|
||||
case 2:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'id_user' => $value]);
|
||||
break;
|
||||
|
||||
// login
|
||||
case 4:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'login' => $value]);
|
||||
break;
|
||||
|
||||
// price level
|
||||
case 5:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'id_price_level' => $value]);
|
||||
break;
|
||||
|
||||
// users groups
|
||||
case 6:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'id_users_group' => $value]);
|
||||
break;
|
||||
|
||||
// region
|
||||
case 11:
|
||||
$this->insertSQL('restrictions_domains', ['id_restriction' => $ID, 'region' => $value]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $SQL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{extends file="[shared]/menu.tpl"}
|
||||
|
||||
{block name="menu-items"}
|
||||
<li class="nav-header"><i class="glyphicon {block list_icon}glyphicon-tags{/block}"></i><span>{translate_type type=$type}</li>
|
||||
<li><a href="javascript:nf('', 'launch.php?s=list.php&type={$type}');"><i class="glyphicon glyphicon-list"></i> <span>{'toolbar_list'|translate}</span></a></li>
|
||||
<li><a href="javascript:nw('{$type}', '0');"><i class="glyphicon glyphicon-plus"></i> <span>{'toolbar_add'|translate}</span></a></li>
|
||||
|
||||
<li class="nav-header smaller"><i class="glyphicon glyphicon-search"></i><span>{'search'|translate}</span></li>
|
||||
<li class="pill-content">
|
||||
<ul class="nav-sub nav-pills">
|
||||
<form id='search' target="mainFrame" method="get" action="launch.php" class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" name="name" value="" placeholder="{'searchName'|translate}"/>
|
||||
<input type="hidden" name="type" value="restrictions" /><input type="hidden" name="s" value="list.php"/>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" border="0" class="btn btn-primary btn-sm" title="Vyhledat"><i class="glyphicon glyphicon-search"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
{/block}
|
||||
@@ -0,0 +1,176 @@
|
||||
{extends file="window.tpl"}
|
||||
|
||||
{block functions append}
|
||||
{function name="inversion" field="" checked=""}
|
||||
<label class="input-group-addon" title="{'inversionInfo'|translate:'filter'}">
|
||||
<input class="input" type="checkbox" name="{$field}_invert" value="1" {$checked}>
|
||||
<span class="bi bi-dash-circle-fill"></span>
|
||||
</label>
|
||||
{/function}
|
||||
{/block}
|
||||
|
||||
{block title}
|
||||
Omezení prodeje
|
||||
{/block}
|
||||
|
||||
{block tabs}
|
||||
{windowTab id='flapRestrictions' label="Omezení prodeje"}
|
||||
{/block}
|
||||
|
||||
{block tabsContent}
|
||||
<div id="flapRestrictions" class="tab-pane fade active in boxStatic">
|
||||
<div class="form-group row-flex">
|
||||
<div class="col-md-3 control-label"><label>{'name'|translate}</label></div>
|
||||
<div class="col-md-9 control-label">
|
||||
<input type="text" class="form-control input-sm" name="data[name]" maxlength="100" value="{$body.data.name}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="h4 main-panel-title">{'domain'|translate}</h1>
|
||||
<div class="form-group">
|
||||
<div class="col-md-3">
|
||||
<select name="data[domain]" class="selecter" id="domain">
|
||||
{foreach $body.restrictions->getDomainNames() as $domain => $key}
|
||||
<option value="{$key}" {if $body.data.domain == $key}selected{/if} data-domain="{$domain}">podle {$domain|translate|lower}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-9" id="domain_values">
|
||||
{foreach $body.restrictions->getDomainNames() as $domain => $key}
|
||||
{$name = "{$domain}_values"}
|
||||
<div class="form-group" data-domain="{$domain}" {if $body.data.domain != $key && !(empty($body.data.id) && $key@first)}style="display: none"{/if}>
|
||||
<div class="col-md-12">
|
||||
<div class="input-group invert">
|
||||
<select name="data[{$name}][]" multiple='multiple' class="selecter" id="{$domain}_select"
|
||||
{if $domain == 'countries' || $domain == 'users_groups' || $domain == 'users'} data-autocomplete="{$domain}" data-preload="{$domain}"{/if}
|
||||
data-type="{$domain}" data-placeholder="Vyhledejte {$domain|translate|lower} ..">
|
||||
{if $domain == 'regions'}
|
||||
{foreach $cfg.Modules.restrictions.regions as $region_name => $id}
|
||||
<option value="{$id}" {if $body.data[$name][$id]}selected{/if}>{$region_name}</option>
|
||||
{/foreach}
|
||||
{elseif $domain == 'countries'}
|
||||
{foreach $body.data.countries_values as $country}
|
||||
<option value="{$country.country}" selected></option>
|
||||
{/foreach}
|
||||
{elseif $domain == 'price_levels'}
|
||||
{foreach $body.data.all_price_levels as $pl}
|
||||
<option value="{$pl.id}" {if $body.data[$name][$pl.id]}selected{/if}>{$pl.name}</option>
|
||||
{/foreach}
|
||||
{elseif $domain == 'login'}
|
||||
<option value="logIn" {if $body.data[$name]['logIn']}selected{/if}>{'login.logIn'|translate:'restrictions'}</option>
|
||||
<option value="logOut" {if $body.data[$name]['logOut']}selected{/if}>{'login.logOut'|translate:'restrictions'}</option>
|
||||
{elseif $domain == 'users_groups'}
|
||||
{foreach $body.data.all_user_groups as $ug}
|
||||
<option value="{$ug.id}" {if $body.data[$name][$ug.id]}selected{/if}>{$ug.name}</option>
|
||||
{/foreach}
|
||||
{else}
|
||||
{foreach $body.data[$name] as $value}
|
||||
<option value="{$value.domain_value}" selected> {if !empty($value.id_user)}{$value.user} {else} {$value.domain_value} {/if}</option>
|
||||
{/foreach}
|
||||
{/if}
|
||||
</select>
|
||||
{inversion field="{$domain}" checked="{if $body.data.domain_invert=='Y'}checked{/if}"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h1 class="h4 main-panel-title">Nastavení omezení</h1>
|
||||
<div class="form-group">
|
||||
<div class="col-md-3 col-md-offset-3 ">
|
||||
<div class="h5 no-margin">
|
||||
<select class="selecter" name="data[type]">
|
||||
<option value="H" {if $body.data.type == 'H'}selected{/if}>{'products_hide'|translate}</option>
|
||||
<option value="S" {if $body.data.type == 'S'}selected{/if}>{'products_show'|translate}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{foreach $body.restrictions->getObjectNames() as $object => $key}
|
||||
<div class="form-group">
|
||||
<div class="col-md-3 control-label">
|
||||
{if $key@first}
|
||||
<label>patřící do {$object|translate|lower}:</label>
|
||||
{else}
|
||||
<label>a zároveň {$object|translate|lower}:</label>
|
||||
{/if}
|
||||
</div>
|
||||
{$name = "{$object}_values"}
|
||||
|
||||
{if $object == 'parameters_list'}
|
||||
{$preload = 'parametersValues'}
|
||||
{/if}
|
||||
|
||||
<div class="col-md-9">
|
||||
<div>
|
||||
{if $preload}
|
||||
<select name="data[{$name}][]" multiple='multiple' class="selecter" data-autocomplete="{$object}"
|
||||
data-preload="{$preload}" data-placeholder="Vyhledejte {$object|translate|lower}... ">
|
||||
{foreach $body.data[$name] as $value}
|
||||
<option value="{$value}" selected> {$value}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
{else}
|
||||
<select name="data[{$name}][]" multiple='multiple' class="selecter" id="{$object}_select" data-type="{$object}"
|
||||
data-placeholder="Vyhledejte {$object|translate|lower}... ">
|
||||
{if $object == 'campaigns'}
|
||||
{foreach $cfg.Products.Flags as $char => $flag}
|
||||
<option value="{$char}" {if $body.data[$name][$char]}selected{/if}>{$flag.plural}</option>
|
||||
{/foreach}
|
||||
{else}
|
||||
{foreach $body.data[$name] as $value}
|
||||
<option value="{$value.id}" selected> {$value.name}</option>
|
||||
{/foreach}
|
||||
{/if}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
<script type="text/javascript">
|
||||
function AddChanger($name){
|
||||
$('#'+$name).change( function(){
|
||||
var $selected = $(this).find('option:selected').data($name);
|
||||
if ($selected){
|
||||
$('#'+$name+'_values').find("[data-"+$name+"][data-"+$name+"!='"+$selected+"']").hide().parent().find("[data-"+$name+"='"+$selected+"']").show();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
AddChanger('domain');
|
||||
|
||||
function applyChosen($selector)
|
||||
{
|
||||
$selector.ajaxChosen({
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
minTermLength: 0,
|
||||
url:'launch.php?s=autocomplete.php&type='+$selector.data('type')
|
||||
},function (data) {
|
||||
return data;
|
||||
}, {
|
||||
width: '100%'
|
||||
});
|
||||
}
|
||||
|
||||
{foreach $body.restrictions->getDomainNames() as $domain => $id}
|
||||
{if !in_array($domain, ['regions', 'countries', 'login', 'price_levels', 'users_groups'])}
|
||||
applyChosen($('#{$domain}_select'));
|
||||
{/if}
|
||||
{/foreach}
|
||||
|
||||
{foreach $body.restrictions->getObjectNames() as $object => $id}
|
||||
{if $object != 'campaigns'}
|
||||
applyChosen($('#{$object}_select'));
|
||||
{/if}
|
||||
{/foreach}
|
||||
|
||||
</script>
|
||||
</div>
|
||||
{/block}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Entity;
|
||||
|
||||
class RestrictionsFilterParams
|
||||
{
|
||||
/***
|
||||
* @param \FilterParams[] $hideFilterParams
|
||||
* @param \FilterParams[] $showFilterParams
|
||||
*/
|
||||
public function __construct(
|
||||
protected array $hideFilterParams = [],
|
||||
protected array $showFilterParams = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FilterParams[]
|
||||
*/
|
||||
public function getHideFilterParams(): array
|
||||
{
|
||||
return $this->hideFilterParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FilterParams[]
|
||||
*/
|
||||
public function getShowFilterParams(): array
|
||||
{
|
||||
return $this->showFilterParams;
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return !$this->hideFilterParams && !$this->showFilterParams;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\EventListener;
|
||||
|
||||
use KupShop\OrderingBundle\Event\CartEvent;
|
||||
use KupShop\OrderingBundle\Exception\CartValidationException;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
use Query\Operator;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class CartItemsListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var Restrictions
|
||||
*/
|
||||
private $restrictions;
|
||||
|
||||
public function __construct(Restrictions $restrictions)
|
||||
{
|
||||
$this->restrictions = $restrictions;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
CartEvent::CHECK_CART_ITEMS => [
|
||||
['checkRestrictions', 200],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function checkRestrictions(CartEvent $event)
|
||||
{
|
||||
$cart = $event->getCart();
|
||||
$cartProducts = array_unique(array_column($cart->products, 'id'));
|
||||
|
||||
$checkProducts = sqlQueryBuilder()
|
||||
->select('p.id')
|
||||
->from('products', 'p')
|
||||
->where(Operator::inIntArray($cartProducts, 'p.id'))
|
||||
->andWhere($this->restrictions->getRestrictionSpec())
|
||||
->execute()->fetchAll();
|
||||
$checkProducts = array_column($checkProducts, 'id');
|
||||
$restrictedProducts = array_diff($cartProducts, $checkProducts);
|
||||
|
||||
if ($restrictedProducts) {
|
||||
$id = reset($restrictedProducts);
|
||||
$cartItem = array_filter($cart->products, function ($i) use ($id) { return $i['id'] == $id; });
|
||||
$title = reset($cartItem)['title'];
|
||||
|
||||
throw new CartValidationException(sprintf(translate('restrictedProducts', 'ordering'), $title));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\EventListener;
|
||||
|
||||
use KupShop\CatalogBundle\Event\FilterParamsEvent;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class FilterParamsListener implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
$events = [];
|
||||
|
||||
if (!findModule(\Modules::PRODUCTS_SECTIONS, \Modules::SUB_ELASTICSEARCH)) {
|
||||
$events[FilterParamsEvent::DEFAULT] = [
|
||||
['addFilterParam', 200],
|
||||
];
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var FilterParamsEvent
|
||||
*/
|
||||
public function addFilterParam(FilterParamsEvent $event)
|
||||
{
|
||||
$restrictionsSpec = ServiceContainer::getService(Restrictions::class);
|
||||
|
||||
$event->addFilterSpec($restrictionsSpec->getRestrictionSpec(), 'restrictions');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\EventListener;
|
||||
|
||||
use KupShop\KupShopBundle\Event\CreateMenuEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class RestrictionsMenuListener implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
CreateMenuEvent::COMPLETING_TREE => [
|
||||
['addItem', 200],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var CreateMenuEvent
|
||||
*/
|
||||
public function addItem(CreateMenuEvent $event)
|
||||
{
|
||||
$event->addItem('productsMenu',
|
||||
[
|
||||
'name' => 'restrictions',
|
||||
'left' => 's=menu.php&type=restrictions', 'right' => 's=list.php&type=restrictions',
|
||||
]
|
||||
);
|
||||
|
||||
$event->addRights('restrictions',
|
||||
[
|
||||
'modules' => [
|
||||
'restrictions',
|
||||
],
|
||||
'rights' => ['RESTRICTIONS'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
KupShop\RestrictionsBundle\:
|
||||
resource: ../../{EventListener,Utils}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
class UpgradeRestrictions extends UpgradeNew
|
||||
{
|
||||
public function check_restrictionsTables()
|
||||
{
|
||||
return $this->checkTableExists('restrictions');
|
||||
}
|
||||
|
||||
/** Create restrictions tables**/
|
||||
public function upgrade_restrictionsTables()
|
||||
{
|
||||
sqlQuery('
|
||||
CREATE TABLE restrictions
|
||||
(
|
||||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
name TEXT DEFAULT NULL,
|
||||
domain INT NOT NULL,
|
||||
object_value TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE restrictions_domains
|
||||
(
|
||||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
id_restriction INT DEFAULT NULL ,
|
||||
id_user INT(11) UNSIGNED DEFAULT NULL,
|
||||
country CHAR(2) DEFAULT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE restrictions_domains ADD CONSTRAINT `restrictions_domains_id_restrictions` FOREIGN KEY (id_restriction) REFERENCES restrictions(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE restrictions_domains ADD CONSTRAINT `restrictions_domains_id_user` FOREIGN KEY (id_user) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
');
|
||||
}
|
||||
|
||||
public function check_restrictionsDomainLogin()
|
||||
{
|
||||
return $this->checkColumnExists('restrictions_domains', 'login');
|
||||
}
|
||||
|
||||
/** Add login column to restrictions **/
|
||||
public function upgrade_restrictionsDomainLogin()
|
||||
{
|
||||
sqlQuery('
|
||||
ALTER TABLE restrictions_domains ADD COLUMN login SET(\'logIn\', \'logOut\') DEFAULT NULL;
|
||||
');
|
||||
}
|
||||
|
||||
public function check_restrictionsDomainPL()
|
||||
{
|
||||
return findModule(Modules::PRICE_LEVELS) && $this->checkColumnExists('restrictions_domains', 'id_price_level');
|
||||
}
|
||||
|
||||
/** Add id_price_level column to restrictions **/
|
||||
public function upgrade_restrictionsDomainPL()
|
||||
{
|
||||
sqlQuery('
|
||||
ALTER TABLE restrictions_domains ADD COLUMN id_price_level INT UNSIGNED DEFAULT NULL;
|
||||
');
|
||||
}
|
||||
|
||||
public function check_idUserGroupColumn()
|
||||
{
|
||||
return $this->checkColumnExists('restrictions_domains', 'id_users_group');
|
||||
}
|
||||
|
||||
/** Add id_users_group into restrictions_domains */
|
||||
public function upgrade_idUserGroupColumn()
|
||||
{
|
||||
sqlQuery('ALTER TABLE restrictions_domains ADD COLUMN id_users_group INT(11) DEFAULT NULL ');
|
||||
sqlQuery('ALTER TABLE restrictions_domains ADD CONSTRAINT FK_id_users_group FOREIGN KEY (id_users_group) REFERENCES users_groups(id) ON DELETE CASCADE ON UPDATE CASCADE');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_domainInvertColumn()
|
||||
{
|
||||
return $this->checkColumnExists('restrictions', 'domain_invert');
|
||||
}
|
||||
|
||||
/** Add domain_invert column into restrictions */
|
||||
public function upgrade_domainInvertColumn()
|
||||
{
|
||||
sqlQuery("ALTER TABLE restrictions ADD COLUMN domain_invert ENUM('Y', 'N') NOT NULL DEFAULT 'N' AFTER domain");
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_restrictionTypeColumn()
|
||||
{
|
||||
return $this->checkColumnExists('restrictions', 'type');
|
||||
}
|
||||
|
||||
/** Restrictions - add type column into restrictions table */
|
||||
public function upgrade_restrictionTypeColumn()
|
||||
{
|
||||
sqlQuery("alter table restrictions add type ENUM ('H', 'S') default 'H' not null comment '(H)ide, (S)how';");
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
20
bundles/KupShop/RestrictionsBundle/RestrictionsBundle.php
Normal file
20
bundles/KupShop/RestrictionsBundle/RestrictionsBundle.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle;
|
||||
|
||||
use KupShop\KupShopBundle\Config;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class RestrictionsBundle extends Bundle
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
$cfg = &Config::get()->getContainer();
|
||||
|
||||
// B2B modul to zapina, ale je potreba to jeste pridat do cfg
|
||||
if (empty($cfg['Modules']['restrictions'])) {
|
||||
$cfg['Modules']['restrictions'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Tests;
|
||||
|
||||
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
|
||||
class RestrictionsProductListTest extends \DatabaseTestCase
|
||||
{
|
||||
use CartTestTrait;
|
||||
|
||||
/** @var ProductList */
|
||||
private $productList;
|
||||
/** @var Restrictions */
|
||||
private $restrictions;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->productList = new \ProductList();
|
||||
$this->restrictions = new Restrictions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataSet_testProductListRestricts
|
||||
*
|
||||
* @param int $domainId
|
||||
* @param int $expectedCount
|
||||
*/
|
||||
public function testProductListRestricts($domainId, $expectedCount)
|
||||
{
|
||||
$this->prepareDomain($domainId);
|
||||
|
||||
$this->productList->applyDefaultFilterParams();
|
||||
$products = $this->productList->getProducts();
|
||||
$this->assertCount($expectedCount, $products);
|
||||
}
|
||||
|
||||
public function dataSet_testProductListRestricts()
|
||||
{
|
||||
return [
|
||||
'exclude category' => [1, 8],
|
||||
'exclude producer' => [2, 9],
|
||||
'exclude campaign' => [3, 7],
|
||||
'exclude product' => [4, 6],
|
||||
'exclude product in campaign' => [6, 9],
|
||||
'empty restriction' => [7, 11],
|
||||
];
|
||||
}
|
||||
|
||||
public function testThrowsOnInvalidJson()
|
||||
{
|
||||
$this->insertSQL('restrictions', [
|
||||
'domain' => 1, // countries
|
||||
'object_value' => 'invalid json should throw',
|
||||
]);
|
||||
|
||||
$this->prepareDomain(sqlGetConnection()->lastInsertId());
|
||||
|
||||
$this->expectException('JsonException');
|
||||
|
||||
$this->productList->applyDefaultFilterParams();
|
||||
$this->productList->getProducts();
|
||||
}
|
||||
|
||||
public function testGetRestrictionSpec()
|
||||
{
|
||||
$this->prepareDomain(4);
|
||||
|
||||
$specs = $this->restrictions->getRestrictionSpec();
|
||||
$this->assertInstanceOf('Closure', $specs);
|
||||
}
|
||||
|
||||
public function testGetRestrictionSpecSql()
|
||||
{
|
||||
$this->prepareDomain(1);
|
||||
|
||||
$snippet = $this->restrictions->getRestrictionSpecSnippet();
|
||||
$this->assertRegExp('/NOT \(EXISTS \(.+\)/', $snippet->where);
|
||||
$this->assertCount(1, $snippet->types);
|
||||
}
|
||||
|
||||
private function prepareDomain($domainId)
|
||||
{
|
||||
$this->insertSQL('restrictions_domains', [
|
||||
'id_restriction' => $domainId,
|
||||
'country' => 'CZ',
|
||||
]);
|
||||
|
||||
$this->loginUser(1);
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
$objects = findModule('restrictions', 'objects');
|
||||
$domains = findModule('restrictions', 'domains');
|
||||
|
||||
// Delete autogenerated product trash-cart
|
||||
$this->deleteSQL('products', ['id' => 0]);
|
||||
|
||||
$arr = [
|
||||
'users' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'country' => 'CZ',
|
||||
],
|
||||
],
|
||||
'restrictions' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([$objects['categories'] => [3]]),
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([$objects['producers'] => [10, 12]]),
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([$objects['campaigns'] => ['N']]),
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([$objects['products'] => [1, 2, 3, 4, 5]]),
|
||||
],
|
||||
[
|
||||
'id' => 5,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([$objects['products'] => [6, 7]]),
|
||||
],
|
||||
[
|
||||
'id' => 6,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => json_encode([
|
||||
$objects['products'] => [8, 11],
|
||||
$objects['campaigns'] => ['N'],
|
||||
]),
|
||||
],
|
||||
[
|
||||
'id' => 7,
|
||||
'domain' => $domains['countries'],
|
||||
'object_value' => '[]',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $this->getJsonDataSet(json_encode($arr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"sections": [
|
||||
{
|
||||
"id": 1,
|
||||
"id_block": null,
|
||||
"name": "Kategorie s produktama",
|
||||
"name_short": "Kategorie s produktama",
|
||||
"figure": "Y",
|
||||
"priority": 0,
|
||||
"behaviour": "2",
|
||||
"orderby": "title",
|
||||
"orderdir": "ASC",
|
||||
"date_updated": "2025-03-28 13:47:07",
|
||||
"list_variations": "N",
|
||||
"lead_figure": "N",
|
||||
"lead_text": "",
|
||||
"lead_products": "",
|
||||
"photo": null,
|
||||
"meta_title": "",
|
||||
"meta_description": "",
|
||||
"meta_keywords": null,
|
||||
"feed_heureka": null,
|
||||
"feed_heureka_sk": null,
|
||||
"feed_google": null,
|
||||
"feed_glami": null,
|
||||
"flags": "",
|
||||
"feed_seznam": null,
|
||||
"producers_filter": "N",
|
||||
"url": null,
|
||||
"redirect_url": null,
|
||||
"data": "{\"enable_sort_by_in_store\": \"Y\", \"enable_sort_by_product_position\": \"Y\", \"filters_inherit_settings\": \"N\"}",
|
||||
"template": "",
|
||||
"virtual": "N",
|
||||
"filter_url": null,
|
||||
"title": null,
|
||||
"producers_indexing": "N",
|
||||
"producers_to_title": "N",
|
||||
"show_in_search": "Y",
|
||||
"labels_filter": "N"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"id_block": null,
|
||||
"name": "Kategorie bez produktu 1",
|
||||
"name_short": "Kategorie bez produktu 1",
|
||||
"figure": "Y",
|
||||
"priority": 0,
|
||||
"behaviour": "2",
|
||||
"orderby": "title",
|
||||
"orderdir": "ASC",
|
||||
"date_updated": "2025-03-28 13:47:22",
|
||||
"list_variations": "N",
|
||||
"lead_figure": "N",
|
||||
"lead_text": "",
|
||||
"lead_products": "",
|
||||
"photo": null,
|
||||
"meta_title": "",
|
||||
"meta_description": "",
|
||||
"meta_keywords": null,
|
||||
"feed_heureka": null,
|
||||
"feed_heureka_sk": null,
|
||||
"feed_google": null,
|
||||
"feed_glami": null,
|
||||
"flags": "",
|
||||
"feed_seznam": null,
|
||||
"annotation": null,
|
||||
"producers_filter": "N",
|
||||
"url": null,
|
||||
"redirect_url": null,
|
||||
"data": null,
|
||||
"template": "",
|
||||
"virtual": "N",
|
||||
"filter_url": null,
|
||||
"title": null,
|
||||
"producers_indexing": "N",
|
||||
"producers_to_title": "N",
|
||||
"show_in_search": "Y",
|
||||
"labels_filter": "N"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"id_block": null,
|
||||
"name": "Kategorie bez produktu 2",
|
||||
"name_short": "Kategorie bez produktu 2",
|
||||
"figure": "Y",
|
||||
"priority": 0,
|
||||
"behaviour": "2",
|
||||
"orderby": "title",
|
||||
"orderdir": "ASC",
|
||||
"date_updated": "2025-03-28 13:46:58",
|
||||
"list_variations": "N",
|
||||
"lead_figure": "N",
|
||||
"lead_text": "",
|
||||
"lead_products": "",
|
||||
"photo": null,
|
||||
"meta_title": "",
|
||||
"meta_description": "",
|
||||
"meta_keywords": null,
|
||||
"feed_heureka": null,
|
||||
"feed_heureka_sk": null,
|
||||
"feed_google": null,
|
||||
"feed_glami": null,
|
||||
"flags": "",
|
||||
"feed_seznam": null,
|
||||
"annotation": null,
|
||||
"producers_filter": "N",
|
||||
"url": null,
|
||||
"redirect_url": null,
|
||||
"data": null,
|
||||
"template": "",
|
||||
"virtual": "N",
|
||||
"filter_url": null,
|
||||
"title": null,
|
||||
"producers_indexing": "N",
|
||||
"producers_to_title": "N",
|
||||
"show_in_search": "Y",
|
||||
"labels_filter": "Y"
|
||||
}
|
||||
],
|
||||
"products_in_sections": [
|
||||
{
|
||||
"id_product": 1,
|
||||
"id_section": 1,
|
||||
"figure": "Y",
|
||||
"generated": 0
|
||||
},
|
||||
{
|
||||
"id_product": 2,
|
||||
"id_section": 1,
|
||||
"figure": "Y",
|
||||
"generated": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Tests;
|
||||
|
||||
use KupShop\CatalogBundle\Section\SectionTree;
|
||||
use KupShop\RestrictionsBundle\Utils\RestrictionUtil;
|
||||
|
||||
class RestrictionsSectionsTest extends \DatabaseTestCase
|
||||
{
|
||||
private RestrictionUtil $restrictionUtil;
|
||||
private SectionTree $sectionTree;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->restrictionUtil = $this->get(RestrictionUtil::class);
|
||||
$this->sectionTree = $this->get(SectionTree::class);
|
||||
}
|
||||
|
||||
/** @dataProvider data_testRestrictSections */
|
||||
public function testRestrictSections(string $showEmptySections, int $expectedCount): void
|
||||
{
|
||||
\Settings::getDefault()->cat_show_empty = $showEmptySections;
|
||||
|
||||
$sections = $this->restrictionUtil->restrictSections(
|
||||
$this->sectionTree->getTree()
|
||||
);
|
||||
|
||||
$this->assertCount($expectedCount, $sections);
|
||||
}
|
||||
|
||||
public function data_testRestrictSections(): iterable
|
||||
{
|
||||
yield 'Show empty sections is disabled' => ['N', 1];
|
||||
yield 'Show empty sections is enabled' => ['Y', 3];
|
||||
}
|
||||
|
||||
protected function getDataSet()
|
||||
{
|
||||
return $this->getJsonDataSetFromFile();
|
||||
}
|
||||
}
|
||||
244
bundles/KupShop/RestrictionsBundle/Tests/RestrictionsTest.php
Normal file
244
bundles/KupShop/RestrictionsBundle/Tests/RestrictionsTest.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Tests;
|
||||
|
||||
use KupShop\ContentBundle\View\ProductView;
|
||||
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
||||
use KupShop\RestrictionsBundle\Utils\Restrictions;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class RestrictionsTest extends \DatabaseTestCase
|
||||
{
|
||||
use CartTestTrait;
|
||||
|
||||
/** @var Restrictions */
|
||||
private $restrictions;
|
||||
|
||||
/** @var ProductView */
|
||||
private $productView;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->restrictions = $this->get(Restrictions::class);
|
||||
$this->productView = $this->get(ProductView::class);
|
||||
}
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
// Omezení Nepřihlášený uživatel - restricted product ID=4
|
||||
// Omezení wpj (id_user = 1,2,3) - restricted product ID=9
|
||||
// Omezení wpjwpj (id_user != 2) - restricted product ID=11
|
||||
|
||||
return $this->getJsonDataSet('
|
||||
{
|
||||
"restrictions": [
|
||||
{
|
||||
"id":3,
|
||||
"name":"Omezení Nepřihlášený uživatel",
|
||||
"domain":4,
|
||||
"domain_invert":"N",
|
||||
"object_value":"{\"4\":[\"4\"]}"
|
||||
},
|
||||
{
|
||||
"id":5,
|
||||
"name":"Omezení wpj",
|
||||
"domain":2,
|
||||
"domain_invert":"N",
|
||||
"object_value":"{\"4\":[\"9\"]}"
|
||||
},
|
||||
{
|
||||
"id":6,
|
||||
"name":"Omezení wpjwpj",
|
||||
"domain":2,
|
||||
"domain_invert":"Y",
|
||||
"object_value":"{\"4\":[\"11\"]}"
|
||||
}
|
||||
],
|
||||
"restrictions_domains": [
|
||||
{
|
||||
"id":210,
|
||||
"id_restriction":3,
|
||||
"id_user":null,
|
||||
"country":null,
|
||||
"login":"logOut",
|
||||
"id_price_level":null,
|
||||
"id_users_group":null
|
||||
},
|
||||
{
|
||||
"id":240,
|
||||
"id_restriction":5,
|
||||
"id_user":1,
|
||||
"country":null,
|
||||
"login":null,
|
||||
"id_price_level":null,
|
||||
"id_users_group":null
|
||||
},
|
||||
{
|
||||
"id":241,
|
||||
"id_restriction":5,
|
||||
"id_user":2,
|
||||
"country":null,
|
||||
"login":null,
|
||||
"id_price_level":null,
|
||||
"id_users_group":null
|
||||
},
|
||||
{
|
||||
"id":242,
|
||||
"id_restriction":5,
|
||||
"id_user":3,
|
||||
"country":null,
|
||||
"login":null,
|
||||
"id_price_level":null,
|
||||
"id_users_group":null
|
||||
},
|
||||
{
|
||||
"id":243,
|
||||
"id_restriction":6,
|
||||
"id_user":2,
|
||||
"country":null,
|
||||
"login":null,
|
||||
"id_price_level":null,
|
||||
"id_users_group":null
|
||||
}
|
||||
]
|
||||
}
|
||||
');
|
||||
}
|
||||
|
||||
/** @dataProvider data_testVisibleProduct1 */
|
||||
public function testVisibleProduct1($id_user, $restricted)
|
||||
{
|
||||
if ($id_user) {
|
||||
$this->loginUser($id_user);
|
||||
}
|
||||
|
||||
$this->productView->setProductId(1);
|
||||
|
||||
if ($restricted) {
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$this->expectExceptionMessage('Product not found');
|
||||
$this->productView->checkProductExist();
|
||||
} else {
|
||||
$product = $this->productView->getProduct();
|
||||
$this->assertNotFalse($product);
|
||||
$this->assertInstanceOf(\Product::class, $product);
|
||||
$this->assertContains('NIKE Capri LACE Test Dlouheho Nazvu Produktu Test Dlouheho Produktu Tes', $product->title);
|
||||
}
|
||||
}
|
||||
|
||||
public function data_testVisibleProduct1()
|
||||
{
|
||||
// product ID=1 is visible for everyone
|
||||
|
||||
return [
|
||||
[1, false],
|
||||
[2, false],
|
||||
[3, false],
|
||||
[null, false],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider data_RestrictedProduct4 */
|
||||
public function testRestrictedProduct4($id_user, $restricted)
|
||||
{
|
||||
if ($id_user) {
|
||||
$this->loginUser($id_user);
|
||||
}
|
||||
|
||||
$this->productView->setProductId(4);
|
||||
|
||||
if ($restricted) {
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$this->expectExceptionMessage('Product not found');
|
||||
$this->productView->checkProductExist();
|
||||
} else {
|
||||
$product = $this->productView->getProduct();
|
||||
$this->assertNotFalse($product);
|
||||
$this->assertInstanceOf(\Product::class, $product);
|
||||
$this->assertContains('Columbia Lay D Down', $product->title);
|
||||
}
|
||||
}
|
||||
|
||||
public function data_RestrictedProduct4()
|
||||
{
|
||||
// Omezení Nepřihlášený uživatel:
|
||||
// product ID=4 is visible for logged in users only
|
||||
|
||||
return [
|
||||
[1, false],
|
||||
[2, false],
|
||||
[3, false],
|
||||
[null, true],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider data_RestrictedProduct9 */
|
||||
public function testRestrictedProduct9($id_user, $restricted)
|
||||
{
|
||||
if ($id_user) {
|
||||
$this->loginUser($id_user);
|
||||
}
|
||||
|
||||
$this->productView->setProductId(9);
|
||||
|
||||
if ($restricted) {
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$this->expectExceptionMessage('Product not found');
|
||||
$this->productView->checkProductExist();
|
||||
} else {
|
||||
$product = $this->productView->getProduct();
|
||||
$this->assertNotFalse($product);
|
||||
$this->assertInstanceOf(\Product::class, $product);
|
||||
$this->assertContains('DeWALT DCD780C2 aku vrtačka', $product->title);
|
||||
}
|
||||
}
|
||||
|
||||
public function data_RestrictedProduct9()
|
||||
{
|
||||
// Omezení wpj (id_user = 1,2,3):
|
||||
// product ID=9 is not visible for wpj users
|
||||
|
||||
return [
|
||||
[1, true],
|
||||
[2, true],
|
||||
[3, true],
|
||||
[null, false],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider data_RestrictedProduct11 */
|
||||
public function testRestrictedProduct11($id_user, $restricted)
|
||||
{
|
||||
if ($id_user) {
|
||||
$this->loginUser($id_user);
|
||||
}
|
||||
|
||||
$this->productView->setProductId(11);
|
||||
|
||||
if ($restricted) {
|
||||
$this->expectException(NotFoundHttpException::class);
|
||||
$this->expectExceptionMessage('Product not found');
|
||||
$this->productView->checkProductExist();
|
||||
} else {
|
||||
$product = $this->productView->getProduct();
|
||||
$this->assertNotFalse($product);
|
||||
$this->assertInstanceOf(\Product::class, $product);
|
||||
$this->assertContains('iPhone wpj', $product->title);
|
||||
}
|
||||
}
|
||||
|
||||
public function data_RestrictedProduct11()
|
||||
{
|
||||
// Omezení wpjwpj (id_user != 2):
|
||||
// product ID=11 is visible for user wpj@wpj.cz only
|
||||
|
||||
return [
|
||||
[1, true],
|
||||
[2, false],
|
||||
[3, true],
|
||||
[null, true],
|
||||
];
|
||||
}
|
||||
}
|
||||
47
bundles/KupShop/RestrictionsBundle/Utils/RestrictionUtil.php
Normal file
47
bundles/KupShop/RestrictionsBundle/Utils/RestrictionUtil.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Utils;
|
||||
|
||||
use KupShop\CatalogBundle\Entity\Section;
|
||||
use KupShop\CatalogBundle\ProductList\FilterParams;
|
||||
use KupShop\CatalogBundle\Util\SectionUtil;
|
||||
|
||||
class RestrictionUtil
|
||||
{
|
||||
public function __construct(
|
||||
private readonly FilterParams $filterParams,
|
||||
private readonly Restrictions $restrictions,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Section[] $sections
|
||||
*
|
||||
* @return Section[]
|
||||
*/
|
||||
public function restrictSections(array $sections): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
// show empty sections is enabled, so we ignore restrictions because all sections should be visible, even if they are empty
|
||||
if ($dbcfg->cat_show_empty === 'Y') {
|
||||
return $sections;
|
||||
}
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('DISTINCT ps.id_section as id')
|
||||
->fromProducts()
|
||||
->joinSectionsOnProducts();
|
||||
|
||||
$qb->andWhere($this->restrictions->getRestrictionSpec());
|
||||
$qb->andWhere($this->filterParams->getSpec());
|
||||
|
||||
$sectionIds = sqlFetchAll($qb, 'id');
|
||||
|
||||
SectionUtil::recurseDeleteSections($sections, $sectionIds);
|
||||
|
||||
return $sections;
|
||||
}
|
||||
}
|
||||
367
bundles/KupShop/RestrictionsBundle/Utils/Restrictions.php
Normal file
367
bundles/KupShop/RestrictionsBundle/Utils/Restrictions.php
Normal file
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\RestrictionsBundle\Utils;
|
||||
|
||||
use KupShop\RestrictionsBundle\Entity\RestrictionsFilterParams;
|
||||
use Query\Filter;
|
||||
use Query\Operator as Op;
|
||||
use Query\Product;
|
||||
|
||||
class Restrictions
|
||||
{
|
||||
/** @var array */
|
||||
private $restrictionSpec;
|
||||
|
||||
/** @var \Raven_Client */
|
||||
private $raven;
|
||||
|
||||
public const TYPE_HIDE = 'H';
|
||||
public const TYPE_SHOW = 'S';
|
||||
|
||||
protected $cfg = [
|
||||
'Modules' => [
|
||||
'restrictions' => [
|
||||
'objects' => [
|
||||
'categories' => 1,
|
||||
'producers' => 2,
|
||||
'campaigns' => 3,
|
||||
'products' => 4,
|
||||
'parameters_list' => 5,
|
||||
],
|
||||
'domains' => [
|
||||
'countries' => 1,
|
||||
'users' => 2,
|
||||
'login' => 4,
|
||||
'price_levels' => 5,
|
||||
'users_groups' => 6,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
public function getRestrictionSpec()
|
||||
{
|
||||
// v adminu - v detailu objednavky ignorujeme restrictions
|
||||
if (isAdministration()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->restrictionSpec === null) {
|
||||
$this->restrictionSpec = $this->createSpec();
|
||||
}
|
||||
|
||||
return $this->restrictionSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Query
|
||||
*/
|
||||
public function getRestrictionSpecSnippet()
|
||||
{
|
||||
$qb = sqlQueryBuilder();
|
||||
|
||||
$query = new \Query();
|
||||
$query->where = (string) $qb->evaluateClosures([$this->getRestrictionSpec()])[0];
|
||||
$query->data = $qb->getParameters();
|
||||
$query->types = $qb->getParameterTypes();
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getDbQuery()
|
||||
{
|
||||
$qb = sqlQueryBuilder()->select('r.id, (r.domain_invert = "Y") AS invert, r.object_value, r.type')
|
||||
->from('restrictions', 'r')
|
||||
->join('r', 'restrictions_domains', 'rd', 'r.id = rd.id_restriction')
|
||||
->groupBy('r.id');
|
||||
|
||||
$find = [];
|
||||
$user = \User::getCurrentUser();
|
||||
|
||||
$find[] = 'WHEN 4 THEN FIND_IN_SET(:login, GROUP_CONCAT(rd.login))';
|
||||
$qb->addParameters(['login' => ($user ? 'logIn' : 'logOut')]);
|
||||
|
||||
// Při registraci v košíku Future uživatel neexistuje a nemáme o něm informace a proto id > 0
|
||||
if ($user && $user->id > 0) {
|
||||
$find[] = 'WHEN 1 THEN FIND_IN_SET(:country, GROUP_CONCAT(rd.country))';
|
||||
$qb->addParameters(['country' => $user->country]);
|
||||
|
||||
$find[] = 'WHEN 2 THEN FIND_IN_SET(:user, GROUP_CONCAT(rd.id_user))';
|
||||
$qb->addParameters(['user' => $user->id]);
|
||||
|
||||
if (findModule(\Modules::PRICE_LEVELS) && $user->getUserPriceLevelId()) {
|
||||
$find[] = 'WHEN 5 THEN FIND_IN_SET(:price_level, GROUP_CONCAT(rd.id_price_level))';
|
||||
$qb->addParameters(['price_level' => $user->getUserPriceLevelId()]);
|
||||
}
|
||||
|
||||
if ($user->getGroups()) {
|
||||
$find_groups = [];
|
||||
foreach ($user->getGroups() as $group) {
|
||||
$find_groups[] = "FIND_IN_SET(:group{$group['id']}, GROUP_CONCAT(rd.id_users_group))";
|
||||
$qb->addParameters(["group{$group['id']}" => $group['id']]);
|
||||
}
|
||||
$find_groups = implode('+', $find_groups);
|
||||
$find[] = "WHEN 6 THEN {$find_groups}";
|
||||
}
|
||||
}
|
||||
|
||||
return [$qb, $find];
|
||||
}
|
||||
|
||||
private function getDbRules(): array
|
||||
{
|
||||
[$qb, $find] = $this->getDbQuery();
|
||||
|
||||
$cacheKey = $this->getCacheKey($qb->getParameters());
|
||||
if ($cachedRules = getCache($cacheKey)) {
|
||||
return $cachedRules;
|
||||
}
|
||||
|
||||
$find = '(CASE r.domain
|
||||
'.implode('
|
||||
', $find).'
|
||||
ELSE 0
|
||||
END) AS find';
|
||||
$qb->addSelect($find);
|
||||
|
||||
$qb->having('(invert XOR find)');
|
||||
|
||||
$ruleRows = $qb->execute()->fetchAllAssociative();
|
||||
setCache($cacheKey, $ruleRows, 30 * 60);
|
||||
|
||||
return $ruleRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $specs default implicit specs
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
protected function createSpec($specs = [])
|
||||
{
|
||||
$ruleRows = $this->getDbRules();
|
||||
|
||||
$typeSpecs = [];
|
||||
|
||||
foreach ($ruleRows as $ruleRow) {
|
||||
$ruleArrays = json_decode_strict($ruleRow['object_value'], true);
|
||||
$rowSpecs = $this->getRuleRowSpecs($ruleArrays);
|
||||
|
||||
if (!$rowSpecs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($ruleRow['type']) {
|
||||
case self::TYPE_HIDE:
|
||||
$typeSpecs[self::TYPE_HIDE][] = Op::not(Op::andX($rowSpecs));
|
||||
break;
|
||||
case self::TYPE_SHOW:
|
||||
$typeSpecs[self::TYPE_SHOW][] = Op::andX($rowSpecs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($typeSpecs[self::TYPE_HIDE] ?? false) {
|
||||
$specs[] = Op::andX($typeSpecs[self::TYPE_HIDE]);
|
||||
}
|
||||
if ($typeSpecs[self::TYPE_SHOW] ?? false) {
|
||||
$specs[] = Op::orX($typeSpecs[self::TYPE_SHOW]);
|
||||
}
|
||||
|
||||
if ($specs) {
|
||||
return Op::andX($specs);
|
||||
}
|
||||
|
||||
return function () {
|
||||
return '1';
|
||||
};
|
||||
}
|
||||
|
||||
private function getRuleRowSpecs($ruleArrays)
|
||||
{
|
||||
$rowSpecs = [];
|
||||
$objectNames = array_flip($this->getObjectNames());
|
||||
|
||||
foreach ($ruleArrays as $objectId => $ruleArray) {
|
||||
$objectName = $objectNames[$objectId];
|
||||
$ruleArray = array_values($ruleArray);
|
||||
|
||||
if (empty($ruleArray)) {
|
||||
// skip empty rules (when restrictions are incorrectly imported)
|
||||
continue;
|
||||
}
|
||||
|
||||
$specName = 'restrict_'.$objectName;
|
||||
if (method_exists($this, $specName)) {
|
||||
$rowSpecs[] = function () use ($specName, $ruleArray) {
|
||||
return call_user_func([$this, $specName], $ruleArray);
|
||||
};
|
||||
} else {
|
||||
$this->getRaven()->captureMessage(
|
||||
"No spec found to restrict results by '%s'.",
|
||||
[$objectName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $rowSpecs;
|
||||
}
|
||||
|
||||
public function getRestrictionFilterParams(): RestrictionsFilterParams
|
||||
{
|
||||
$ruleRows = $this->getDbRules();
|
||||
$filterParamsByType = [];
|
||||
|
||||
foreach ($ruleRows as $ruleRow) {
|
||||
$ruleArrays = json_decode_strict($ruleRow['object_value'], 1);
|
||||
$filterParams = $this->getRuleRowFilterParams($ruleArrays);
|
||||
|
||||
if (!$filterParams) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filterParamsByType[$ruleRow['type']][] = $filterParams;
|
||||
}
|
||||
|
||||
return new RestrictionsFilterParams($filterParamsByType[self::TYPE_HIDE] ?? [], $filterParamsByType[self::TYPE_SHOW] ?? []);
|
||||
}
|
||||
|
||||
private function getRuleRowFilterParams($ruleArrays)
|
||||
{
|
||||
$filterParams = new \FilterParams();
|
||||
|
||||
$objectNames = array_flip($this->getObjectNames());
|
||||
|
||||
foreach ($ruleArrays as $objectId => $ruleArray) {
|
||||
$objectName = $objectNames[$objectId];
|
||||
$ruleArray = array_values($ruleArray);
|
||||
switch ($objectName) {
|
||||
case 'categories':
|
||||
$filterParams->setSections($ruleArray);
|
||||
break;
|
||||
case 'producers':
|
||||
$filterParams->setProducers($ruleArray);
|
||||
break;
|
||||
case 'campaigns':
|
||||
foreach ($ruleArray as $campaignId) {
|
||||
$filterParams->setCampaign($campaignId);
|
||||
}
|
||||
break;
|
||||
case 'products':
|
||||
$filterParams->setProducts($ruleArray);
|
||||
break;
|
||||
case 'parameters_list':
|
||||
$paramsListParamIds = sqlQueryBuilder()->select('id, id_parameter')->from('parameters_list')
|
||||
->where(Op::inIntArray($ruleArray, 'id'));
|
||||
|
||||
$paramsIndex = [];
|
||||
foreach ($paramsListParamIds->execute() as $param) {
|
||||
$paramsIndex[$param['id_parameter']][] = $param['id'];
|
||||
}
|
||||
foreach ($paramsIndex as $paramId => $paramValues) {
|
||||
$filterParams->setParameter($paramId, $paramValues);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $filterParams;
|
||||
}
|
||||
|
||||
public function getObjectNames()
|
||||
{
|
||||
return array_merge($this->cfg['Modules']['restrictions']['objects'], findModule('restrictions', 'objects', []));
|
||||
}
|
||||
|
||||
public function getDomainNames()
|
||||
{
|
||||
return array_merge($this->cfg['Modules']['restrictions']['domains'], findModule('restrictions', 'domains', []));
|
||||
}
|
||||
|
||||
protected function restrict_categories(array $categoryIds)
|
||||
{
|
||||
return Product::inSections($categoryIds, 'rps');
|
||||
}
|
||||
|
||||
protected function restrict_producers(array $producerIds)
|
||||
{
|
||||
return Op::andX(
|
||||
Filter::byProducers($producerIds),
|
||||
'pr.id is not null'
|
||||
);
|
||||
}
|
||||
|
||||
protected function restrict_campaigns(array $campaigns)
|
||||
{
|
||||
return Filter::byCampaigns($campaigns, 'OR');
|
||||
}
|
||||
|
||||
protected function restrict_products(array $productIds)
|
||||
{
|
||||
return Op::inIntArray($productIds, 'p.id');
|
||||
}
|
||||
|
||||
protected function restrict_parameters_list(array $parameterIds)
|
||||
{
|
||||
$filterCallback = function ($alias, $paramId, $paramData) {
|
||||
return Op::equals(["{$alias}.value_list" => $paramData]);
|
||||
};
|
||||
|
||||
$parameters = array_combine($parameterIds, $parameterIds);
|
||||
|
||||
return Filter::parametersFilterUtil($parameters, $filterCallback, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Raven_Client
|
||||
*/
|
||||
public function getRaven()
|
||||
{
|
||||
if (!$this->raven) {
|
||||
$this->raven = getRaven();
|
||||
}
|
||||
|
||||
return $this->raven;
|
||||
}
|
||||
|
||||
public function setRaven(\Raven_Client $raven)
|
||||
{
|
||||
$this->raven = $raven;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query array containing at least 'data', 'where' and 'types' keys
|
||||
*
|
||||
* @return array modified query
|
||||
*/
|
||||
public function applyToOldQuery($query)
|
||||
{
|
||||
$restrictions = $this->getRestrictionSpecSnippet();
|
||||
|
||||
$query['data'] = array_merge(
|
||||
$query['data'],
|
||||
$restrictions->data
|
||||
);
|
||||
$query['types'] = array_merge(
|
||||
$query['types'],
|
||||
$restrictions->types
|
||||
);
|
||||
$query['where'] .= " AND ({$restrictions->where})";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function getCacheKey(array $parameters): string
|
||||
{
|
||||
$cacheKey = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
$cacheKey .= '_'.$key.'-'.$value;
|
||||
}
|
||||
|
||||
return 'restrictions'.$cacheKey;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user