refactor(shop_category): migrate admin module to Domain+DI with routing and ajax cleanup
This commit is contained in:
3
temp/update_build/delete_files_0.275.txt
Normal file
3
temp/update_build/delete_files_0.275.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
autoload/admin/controls/class.ShopCategory.php
|
||||
autoload/admin/factory/class.ShopCategory.php
|
||||
autoload/admin/view/class.ShopCategory.php
|
||||
15
temp/update_build/tmp_0.275/admin/ajax/shop-category.php
Normal file
15
temp/update_build/tmp_0.275/admin/ajax/shop-category.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
$a = \S::get( 'a' );
|
||||
|
||||
if ( $a == 'save_products_order' )
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kolejności wyświetlania produktów wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
$categoryRepository = new \Domain\Category\CategoryRepository( $mdb );
|
||||
|
||||
if ( $categoryRepository->saveProductOrder( \S::get( 'category_id' ), \S::get( 'products' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
60
temp/update_build/tmp_0.275/admin/ajax/shop.php
Normal file
60
temp/update_build/tmp_0.275/admin/ajax/shop.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
if ( $a == 'cookie_categories' )
|
||||
{
|
||||
$array = unserialize( $_COOKIE[ 'cookie_categories' ] );
|
||||
|
||||
if ( $array[ \S::get( 'category_id' ) ] == 0 )
|
||||
$array[ \S::get( 'category_id' ) ] = 1;
|
||||
else
|
||||
$array[ \S::get( 'category_id' ) ] = 0;
|
||||
|
||||
$array = serialize( $array );
|
||||
|
||||
setcookie( 'cookie_categories', $array, time() + 3600 * 24 * 365 );
|
||||
}
|
||||
|
||||
if ( $a == 'save_categories_order' )
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kolejności kategorii wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
$categoryRepository = new \Domain\Category\CategoryRepository( $mdb );
|
||||
|
||||
if ( $categoryRepository->saveCategoriesOrder( \S::get( 'categories' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $a == 'product_file_delete' )
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas usuwania załącznika wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::delete_file( \S::get( 'file_id' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $a == 'product_file_name_change' )
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany nazwy załącznika wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::file_name_change( \S::get( 'file_id' ), \S::get( 'file_name' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $a == 'product_image_delete' )
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas usuwania zdjecia wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::delete_img( \S::get( 'image_id' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<style type="text/css">
|
||||
.layout-tree-toggle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-right: 4px;
|
||||
margin-top: -2px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.layout-tree-toggle i.fa.fa-caret-right {
|
||||
position: relative;
|
||||
top: -11px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.layout-tree-toggle:focus,
|
||||
.layout-tree-toggle:active,
|
||||
.layout-tree-toggle:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
li.sort-expanded > div .layout-tree-toggle i {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="/libraries/jquery-nested-sortable/jquery.mjs.nestedSortable.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
function refreshTreeDisclosureState() {
|
||||
$('ol.sortable li').each(function() {
|
||||
var $li = $(this);
|
||||
var hasChildren = $li.children('ol').children('li').length > 0;
|
||||
var $disclose = $li.children('div').children('.disclose');
|
||||
|
||||
if (hasChildren) {
|
||||
$li.removeClass('sort-leaf');
|
||||
if (!$li.hasClass('sort-collapsed') && !$li.hasClass('sort-expanded'))
|
||||
$li.addClass('sort-collapsed');
|
||||
$li.addClass('sort-branch');
|
||||
$disclose.attr('aria-expanded', $li.hasClass('sort-expanded') ? 'true' : 'false');
|
||||
$disclose.show();
|
||||
} else {
|
||||
$li.removeClass('sort-branch sort-collapsed sort-expanded').addClass('sort-leaf');
|
||||
$disclose.attr('aria-expanded', 'false');
|
||||
$disclose.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveCategoriesOrder() {
|
||||
var categories = $('#sortable').nestedSortable('toArray', { startDepthCount: 0 });
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: '/admin/shop_category/save_categories_order/',
|
||||
data: {
|
||||
categories: categories
|
||||
},
|
||||
beforeSend: function() {
|
||||
$.prompt('Trwa zapisywanie...', { title: 'Proszę czekać' });
|
||||
},
|
||||
success: function(data) {
|
||||
$('.jqibox').remove();
|
||||
var response = jQuery.parseJSON(data);
|
||||
|
||||
if (response.status === 'error')
|
||||
create_error(response.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('body').on('click', '.category-delete', function() {
|
||||
var category_id = $(this).attr('category-id');
|
||||
|
||||
$.prompt('Na pewno chcesz usunąć wybraną kategorię?', {
|
||||
title: 'Potwierdź?',
|
||||
buttons: { 'Tak': true, 'Nie': false },
|
||||
submit: function(e, v) {
|
||||
if (v === true)
|
||||
document.location.href = '/admin/shop_category/category_delete/id=' + category_id;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('ol.sortable').nestedSortable({
|
||||
forcePlaceholderSize: true,
|
||||
handle: 'div',
|
||||
helper: 'clone',
|
||||
items: 'li',
|
||||
opacity: .9,
|
||||
placeholder: 'placeholder',
|
||||
revert: 250,
|
||||
tabSize: 45,
|
||||
tolerance: 'pointer',
|
||||
toleranceElement: '> div',
|
||||
maxLevels: 4,
|
||||
isTree: true,
|
||||
expandOnHover: 700,
|
||||
protectRoot: false,
|
||||
stop: function() {
|
||||
saveCategoriesOrder();
|
||||
}
|
||||
});
|
||||
|
||||
refreshTreeDisclosureState();
|
||||
|
||||
$('body').on('click', '.disclose', function() {
|
||||
var $li = $(this).closest('li');
|
||||
if (!$li.hasClass('sort-branch')) return;
|
||||
$li.toggleClass('sort-collapsed').toggleClass('sort-expanded');
|
||||
$(this).attr('aria-expanded', $li.hasClass('sort-expanded') ? 'true' : 'false');
|
||||
this.blur();
|
||||
});
|
||||
|
||||
$('.disclose').mousedown(function(e) {
|
||||
if (e.which === 1) {
|
||||
var category_id = $(this).parent('div').parent('li').attr('id');
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: '/admin/shop_category/cookie_categories/',
|
||||
data: {
|
||||
category_id: category_id
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
<?php
|
||||
$array = isset($_COOKIE['cookie_categories']) ? @unserialize($_COOKIE['cookie_categories']) : [];
|
||||
if (is_array($array)):
|
||||
foreach ($array as $key => $val):
|
||||
if ($val):
|
||||
?>
|
||||
$('#<?= $key; ?>').children('div').children('button.disclose').click();
|
||||
<?php
|
||||
endif;
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,61 @@
|
||||
<?
|
||||
global $gdb;
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="menu_sortable">
|
||||
<ol class="sortable" id="sortable">
|
||||
<? if ( is_array( $this -> categories ) ): foreach ( $this -> categories as $category ):?>
|
||||
<li id="list_<?= $category['id'];?>" class="category_<?= $category['id'];?>" category="<?= $category['id'];?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<? if ( !$category['status'] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>';?>
|
||||
<b><?= $category['languages'][$this -> dlang]['title'];?></b>
|
||||
<div class="btn-group ml20 pull-right">
|
||||
<a href="/admin/shop_category/category_edit/id=<?= $category['id'];?>" title="Edytuj kategorię" class="btn btn-sm btn-system">
|
||||
<i class="fa fa-file-text"></i> <span class="hidden-xs">Edytuj kategorię</span>
|
||||
</a>
|
||||
<a href="#" title="Usuń kategorię" class="btn btn-sm btn-danger category-delete" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-trash"></i> <span class="hidden-xs">Usuń kategorię</span>
|
||||
</a>
|
||||
<a href="/admin/shop_category/category_products/id=<?= $category['id'];?>" title="Lista produktów" class="btn btn-sm btn-info" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-bars"></i> <span class="hidden-xs">Lista produków</span>
|
||||
</a>
|
||||
<a href="/admin/shop_category/category_edit/pid=<?= $category['id'];?>" title="Dodaj podkategorię" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-plus-circle"></i> <span class="hidden-xs">Dodaj podkategorię</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?= \Tpl::view( 'shop-category/subcategories-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category['id'] ),
|
||||
'level' => $this -> level + 1,
|
||||
'dlang' => $this -> dlang
|
||||
] );?>
|
||||
</li>
|
||||
<? endforeach; endif;?>
|
||||
</ol>
|
||||
</div>
|
||||
<?
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid -> id = 'pages-list';
|
||||
$grid -> gdb_opt = $gdb;
|
||||
$grid -> include_plugins = true;
|
||||
$grid -> title = 'Lista kategorii';
|
||||
$grid -> default_buttons = false;
|
||||
$grid -> buttons = [
|
||||
[
|
||||
'label' => 'Dodaj kategorię',
|
||||
'url' => '/admin/shop_category/category_edit/',
|
||||
'icon' => 'fa-plus-circle',
|
||||
'class' => 'btn-success'
|
||||
]
|
||||
];
|
||||
$grid -> external_code = $out;
|
||||
|
||||
echo $grid -> draw();
|
||||
?>
|
||||
<?= \Tpl::view( 'shop-category/categories-list-custom-script' ); ?>
|
||||
@@ -0,0 +1,106 @@
|
||||
<style type="text/css">
|
||||
.layout-tree-toggle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-right: 4px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layout-tree-toggle:focus,
|
||||
.layout-tree-toggle:active,
|
||||
.layout-tree-toggle:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
li.sort-expanded > div .layout-tree-toggle i {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="/libraries/jquery-nested-sortable/jquery.mjs.nestedSortable.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
function refreshTreeDisclosureState() {
|
||||
$('ol.sortable li').each(function() {
|
||||
var $li = $(this);
|
||||
var hasChildren = $li.children('ol').children('li').length > 0;
|
||||
var $disclose = $li.children('div').children('.disclose');
|
||||
|
||||
if (hasChildren) {
|
||||
$li.removeClass('sort-leaf');
|
||||
if (!$li.hasClass('sort-collapsed') && !$li.hasClass('sort-expanded'))
|
||||
$li.addClass('sort-collapsed');
|
||||
$li.addClass('sort-branch');
|
||||
$disclose.attr('aria-expanded', $li.hasClass('sort-expanded') ? 'true' : 'false');
|
||||
$disclose.show();
|
||||
} else {
|
||||
$li.removeClass('sort-branch sort-collapsed sort-expanded').addClass('sort-leaf');
|
||||
$disclose.attr('aria-expanded', 'false');
|
||||
$disclose.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('ol.sortable').nestedSortable({
|
||||
forcePlaceholderSize: true,
|
||||
handle: 'div',
|
||||
helper: 'clone',
|
||||
items: 'li',
|
||||
opacity: .9,
|
||||
placeholder: 'placeholder',
|
||||
revert: 250,
|
||||
tabSize: 45,
|
||||
tolerance: 'pointer',
|
||||
toleranceElement: '> div',
|
||||
maxLevels: 4,
|
||||
isTree: true,
|
||||
expandOnHover: 700,
|
||||
protectRoot: false
|
||||
});
|
||||
|
||||
refreshTreeDisclosureState();
|
||||
|
||||
$('body').on('click', '.disclose', function() {
|
||||
var $li = $(this).closest('li');
|
||||
if (!$li.hasClass('sort-branch')) return;
|
||||
$li.toggleClass('sort-collapsed').toggleClass('sort-expanded');
|
||||
$(this).attr('aria-expanded', $li.hasClass('sort-expanded') ? 'true' : 'false');
|
||||
this.blur();
|
||||
});
|
||||
|
||||
$('.disclose').mousedown(function(e) {
|
||||
if (e.which === 1) {
|
||||
var category_id = $(this).parent('div').parent('li').attr('id');
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: '/admin/shop_category/cookie_categories/',
|
||||
data: {
|
||||
category_id: category_id
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
<?php
|
||||
$array = isset($_COOKIE['cookie_categories']) ? @unserialize($_COOKIE['cookie_categories']) : [];
|
||||
if (is_array($array)):
|
||||
foreach ($array as $key => $val):
|
||||
if ($val):
|
||||
?>
|
||||
$('#<?= $key; ?>').children('div').children('button.disclose').click();
|
||||
<?php
|
||||
endif;
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?
|
||||
global $gdb;
|
||||
ob_start();
|
||||
?>
|
||||
<div class="menu_sortable">
|
||||
<ol class="sortable" id="sortable">
|
||||
<? if ( is_array( $this -> categories ) ): foreach ( $this -> categories as $category ):?>
|
||||
<li id="list_<?= $category['id'];?>" class="category_<?= $category['id'];?>" category="<?= $category['id'];?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<? if ( !$category['status'] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>';?>
|
||||
<b><?= $category['languages'][$this -> dlang]['title'];?></b>
|
||||
<div class="btn-group ml20 pull-right">
|
||||
<a href="#" title="Wybierz kategorię" class="btn btn-sm btn-system button category-select" category-title="<?= $category['languages'][$this -> dlang]['title'];?>" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-check"></i> <span class="hidden-xs">wybierz</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?= \Tpl::view( 'shop-category/subcategory-browse-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category['id'] ),
|
||||
'level' => $this -> level + 1,
|
||||
'dlang' => $this -> dlang
|
||||
] );?>
|
||||
</li>
|
||||
<? endforeach; endif;?>
|
||||
</ol>
|
||||
</div>
|
||||
<?
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid -> id = 'pages-list';
|
||||
$grid -> gdb_opt = $gdb;
|
||||
$grid -> include_plugins = false;
|
||||
$grid -> title = 'Lista kategorii';
|
||||
$grid -> default_buttons = false;
|
||||
$grid -> external_code = $out;
|
||||
|
||||
echo $grid -> draw();
|
||||
?>
|
||||
<style type="text/css">
|
||||
.mfp-container body.sb-top.sb-top-sm .navbar.navbar-fixed-top + #sidebar_left + #content_wrapper {
|
||||
padding-top: 0;
|
||||
}
|
||||
.mfp-container #content {
|
||||
padding: 27px 0 0;
|
||||
}
|
||||
.mfp-container #content > .row {
|
||||
margin: 0;
|
||||
}
|
||||
.mfp-container #content > .row > .col-lg-9 {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
</style>
|
||||
<?= \Tpl::view( 'shop-category/category-browse-custom-script' ); ?>
|
||||
@@ -0,0 +1,53 @@
|
||||
<script type="text/javascript">
|
||||
$( function() {
|
||||
disable_menu();
|
||||
|
||||
$( '#settings-tabs' ).easyResponsiveTabs({
|
||||
width: 'auto',
|
||||
fit: true,
|
||||
tabidentify: 'settings-tabs',
|
||||
type: 'vertical'
|
||||
});
|
||||
|
||||
$( '#languages-main' ).easyResponsiveTabs({
|
||||
width: 'auto',
|
||||
fit: true,
|
||||
tabidentify: 'languages-main'
|
||||
});
|
||||
|
||||
$( '#languages-seo' ).easyResponsiveTabs({
|
||||
width: 'auto',
|
||||
fit: true,
|
||||
tabidentify: 'languages-seo'
|
||||
});
|
||||
});
|
||||
|
||||
function generate_seo_links( lang, title, category_id )
|
||||
{
|
||||
if ( title === '' )
|
||||
return false;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: '/admin/pages/generateSeoLink/',
|
||||
data: {
|
||||
title: title,
|
||||
category_id: category_id
|
||||
},
|
||||
beforeSend: function() {
|
||||
$( '#overlay' ).show();
|
||||
},
|
||||
success: function( data ) {
|
||||
$( '#overlay' ).hide();
|
||||
|
||||
var response = jQuery.parseJSON( data );
|
||||
|
||||
if ( response.status === 'ok' )
|
||||
$( '#seo_link_' + lang ).val( response.seo_link );
|
||||
else
|
||||
create_error( response.msg );
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,209 @@
|
||||
<script type="text/javascript" src="/libraries/ckeditor/ckeditor.js"></script>
|
||||
<script type="text/javascript" src="/libraries/ckeditor/adapters/jquery.js"></script>
|
||||
<?
|
||||
global $db;
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div id="settings-tabs">
|
||||
<ul class="resp-tabs-list settings-tabs">
|
||||
<li><i class="fa fa-file"></i>Treść</li>
|
||||
<li><i class="fa fa-wrench"></i>Ustawienia</li>
|
||||
<li><i class="fa fa-globe"></i>SEO</li>
|
||||
</ul>
|
||||
<div class="resp-tabs-container settings-tabs">
|
||||
<div>
|
||||
<div id="languages-main">
|
||||
<ul class="resp-tabs-list languages-main htabs">
|
||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||
<? if ( $lg['status'] ):?>
|
||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
||||
<? endif;?>
|
||||
<? endforeach; endif;?>
|
||||
</ul>
|
||||
<div class="resp-tabs-container languages-main">
|
||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||
<? if ( $lg['status'] ):?>
|
||||
<div>
|
||||
<?= \Html::input(
|
||||
array(
|
||||
'label' => 'Nazwa kategorii',
|
||||
'name' => 'title[' . $lg['id'] . ']',
|
||||
'id' => 'title_' . $lg['id'],
|
||||
'value' => $this -> category[ 'languages' ][ $lg['id'] ]['title'],
|
||||
'inline' => true
|
||||
)
|
||||
);?>
|
||||
<?= \Html::textarea(
|
||||
array(
|
||||
'label' => 'Opis kategorii',
|
||||
'name' => 'text[' . $lg['id'] . ']',
|
||||
'id' => 'text_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['text'],
|
||||
'inline' => true
|
||||
)
|
||||
);?>
|
||||
<?= \Html::textarea(
|
||||
array(
|
||||
'label' => 'Opis kategorii (rozwinięcie)',
|
||||
'name' => 'text_hidden[' . $lg['id'] . ']',
|
||||
'id' => 'text_hidden_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['text_hidden'],
|
||||
'inline' => true
|
||||
)
|
||||
);?>
|
||||
<?= \Html::textarea( [
|
||||
'label' => 'Dodatkowy tekst (nad produktami)',
|
||||
'name' => 'additional_text[' . $lg['id'] . ']',
|
||||
'id' => 'additional_text_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['additional_text'],
|
||||
'inline' => true
|
||||
] );?>
|
||||
<script type="text/javascript">
|
||||
$( function() {
|
||||
$( '#text_<?= $lg['id'];?>, #text_hidden_<?= $lg['id'];?>, #additional_text_<?= $lg['id'];?>' ).ckeditor( {
|
||||
toolbar : 'MyToolbar',
|
||||
height:'250'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<? endforeach; endif;?>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<?= \Html::input_switch(
|
||||
array(
|
||||
'label' => 'Aktywna',
|
||||
'name' => 'status',
|
||||
'checked' => $this -> category['status'] == 1 or !$this -> category['id'] ? true : false
|
||||
)
|
||||
);?>
|
||||
<?= \Html::select(
|
||||
[
|
||||
'label' => 'Sortowanie produktĂłw',
|
||||
'name' => 'sort_type',
|
||||
'id' => 'sort_type',
|
||||
'values' => is_array( $this -> sort_types ) ? $this -> sort_types : [],
|
||||
'value' => $this -> category['sort_type']
|
||||
]
|
||||
);?>
|
||||
<?= \Html::input_switch(
|
||||
array(
|
||||
'label' => 'Wyświetlić podkategorie',
|
||||
'name' => 'view_subcategories',
|
||||
'checked' => $this -> category['view_subcategories'] == 1 ? true : false
|
||||
)
|
||||
);?>
|
||||
</div>
|
||||
<div>
|
||||
<div id="languages-seo">
|
||||
<ul class="resp-tabs-list languages-seo htabs">
|
||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||
<? if ( $lg['status'] ):?>
|
||||
<li><? if ( $lg['id'] == \front\factory\Languages::default_language() ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
|
||||
<? endif;?>
|
||||
<? endforeach; endif;?>
|
||||
</ul>
|
||||
<div class="resp-tabs-container languages-seo">
|
||||
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
|
||||
<? if ( $lg['status'] ):?>
|
||||
<div>
|
||||
<?= \Html::input_icon(
|
||||
array(
|
||||
'label' => 'Link SEO',
|
||||
'name' => 'seo_link[' . $lg['id'] . ']',
|
||||
'id' => 'seo_link_' . $lg['id'],
|
||||
'value' => $this -> category['languages' ][ $lg['id'] ]['seo_link'],
|
||||
'icon_content' => 'generuj',
|
||||
'icon_js' => 'generate_seo_links( "' . $lg['id'] . '", $( "#title_' . $lg['id'] . '" ).val(), ' . (int)$this -> category['id'] . ' );'
|
||||
)
|
||||
);?>
|
||||
<?= \Html::input(
|
||||
array(
|
||||
'label' => 'Tytuł kategorii (h1)',
|
||||
'name' => 'category_title[' . $lg['id'] . ']',
|
||||
'id' => 'category_title_' . $lg['id'],
|
||||
'value' => $this -> category['languages' ][ $lg['id'] ]['category_title']
|
||||
)
|
||||
);?>
|
||||
<?= \Html::input(
|
||||
array(
|
||||
'label' => 'Meta title',
|
||||
'name' => 'meta_title[' . $lg['id'] . ']',
|
||||
'id' => 'meta_title_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['meta_title']
|
||||
)
|
||||
);?>
|
||||
<?= \Html::textarea(
|
||||
array(
|
||||
'label' => 'Meta description',
|
||||
'name' => 'meta_description[' . $lg['id'] . ']',
|
||||
'id' => 'meta_description_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['meta_description']
|
||||
)
|
||||
);?>
|
||||
<?= \Html::textarea(
|
||||
array(
|
||||
'label' => 'Meta keywords',
|
||||
'name' => 'meta_keywords[' . $lg['id'] . ']',
|
||||
'id' => 'meta_keywords_' . $lg['id'],
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['meta_keywords']
|
||||
)
|
||||
);?>
|
||||
<?= \Html::select(
|
||||
array(
|
||||
'label' => 'Blokuj indeksacjÄ™',
|
||||
'name' => 'noindex[' . $lg['id'] . ']',
|
||||
'id' => 'noindex_' . $lg['id'],
|
||||
'values' => array(
|
||||
0 => 'nie', 1 => 'tak'
|
||||
),
|
||||
'value' => $this -> category['languages'][ $lg['id'] ]['noindex'] == 1 ? 1 : 0
|
||||
)
|
||||
);?>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<? endforeach; endif;?>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<?
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid -> id = 'category-edit';
|
||||
$grid -> gdb_opt = $gdb;
|
||||
$grid -> include_plugins = true;
|
||||
$grid -> title = 'Edycja kategorii';
|
||||
$grid -> fields = [
|
||||
[
|
||||
'db' => 'id',
|
||||
'type' => 'hidden',
|
||||
'value' => $this -> category['id']
|
||||
],
|
||||
[
|
||||
'db' => 'parent_id',
|
||||
'type' => 'hidden',
|
||||
'value' => $this -> category['id'] ? $this -> category['parent_id'] : $this -> pid
|
||||
]
|
||||
];
|
||||
$grid -> actions = [
|
||||
'save' => [ 'url' => '/admin/shop_category/save/', 'back_url' => '/admin/shop_category/view_list/' ],
|
||||
'cancel' => [ 'url' => '/admin/shop_category/view_list/' ]
|
||||
];
|
||||
$grid -> external_code = $out;
|
||||
$grid -> persist_edit = true;
|
||||
$grid -> id_param = 'id';
|
||||
|
||||
echo $grid -> draw();
|
||||
?>
|
||||
|
||||
<?= \Tpl::view( 'shop-category/category-edit-custom-script' ); ?>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<style type="text/css">
|
||||
.layout-tree-toggle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-right: 6px;
|
||||
color: #666;
|
||||
cursor: move;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="/libraries/jquery-nested-sortable/jquery.mjs.nestedSortable.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
function saveProductsOrder() {
|
||||
var products = $('ol.sortable').nestedSortable('toArray', { startDepthCount: 0 });
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
url: '/admin/shop_category/save_products_order/',
|
||||
data: {
|
||||
category_id: <?= (int)$this->category_id; ?>,
|
||||
products: products
|
||||
},
|
||||
beforeSend: function() {
|
||||
$.prompt('Trwa zapisywanie...', { title: 'Proszę czekać' });
|
||||
},
|
||||
success: function(data) {
|
||||
$('.jqibox').remove();
|
||||
var response = jQuery.parseJSON(data);
|
||||
|
||||
if (response.status === 'error')
|
||||
create_error(response.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('ol.sortable').nestedSortable({
|
||||
forcePlaceholderSize: true,
|
||||
handle: 'div',
|
||||
helper: 'clone',
|
||||
items: 'li',
|
||||
opacity: .6,
|
||||
placeholder: 'placeholder',
|
||||
revert: 250,
|
||||
tabSize: 25,
|
||||
tolerance: 'pointer',
|
||||
toleranceElement: '> div',
|
||||
maxLevels: 1,
|
||||
isTree: true,
|
||||
expandOnHover: 700,
|
||||
stop: function() {
|
||||
saveProductsOrder();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?
|
||||
global $gdb;
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<ol class="sortable" id="article-list">
|
||||
<?
|
||||
if ( is_array( $this -> products ) ) foreach ( $this -> products as $product )
|
||||
{
|
||||
?>
|
||||
<li id="list_<?= $product['product_id'];?>">
|
||||
<div class="content <? if ( !$product['status'] ) echo 'text-danger';?>"><button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Przesuń"><i class="fa fa-arrows"></i></button> <?= $product['name'];?></div>
|
||||
</li>
|
||||
<?
|
||||
}
|
||||
?>
|
||||
</ol>
|
||||
<?
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid -> gdb_opt = $gdb;
|
||||
$grid -> include_plugins = true;
|
||||
$grid -> default_buttons = false;
|
||||
$grid -> external_code = $out;
|
||||
$grid -> title = 'Lista produktów';
|
||||
$grid -> buttons = [
|
||||
[
|
||||
'label' => 'Wstecz',
|
||||
'url' => '/admin/shop_category/view_list/',
|
||||
'icon' => 'fa-reply',
|
||||
'class' => 'btn-dark'
|
||||
]
|
||||
];
|
||||
echo $grid -> draw();
|
||||
?>
|
||||
<?= \Tpl::view( 'shop-category/category-products-custom-script', [ 'category_id' => $this -> category_id ] ); ?>
|
||||
@@ -0,0 +1,36 @@
|
||||
<? if ( is_array( $this -> categories ) ):?>
|
||||
<ol>
|
||||
<? foreach ( $this -> categories as $category ):?>
|
||||
<li id="list_<?= $category['id'];?>" class="list_<?= $category['id'];?>" category="<?= $category['id'];?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<? if ( !$category['status'] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>';?>
|
||||
<b><?= $category['languages'][$this -> dlang]['title'];?></b>
|
||||
<div class="btn-group ml20 pull-right">
|
||||
<a href="/admin/shop_category/category_edit/id=<?= $category['id'];?>" title="Edytuj kategorię" class="btn btn-sm btn-system">
|
||||
<i class="fa fa-file-text"></i> <span class="hidden-xs">Edytuj kategorię</span>
|
||||
</a>
|
||||
<a href="#" title="Usuń kategorię" class="btn btn-sm btn-danger category-delete" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-trash"></i> <span class="hidden-xs">Usuń kategorię</span>
|
||||
</a>
|
||||
<a href="/admin/shop_category/category_products/id=<?= $category['id'];?>" title="Lista produktów" class="btn btn-sm btn-info" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-bars"></i> <span class="hidden-xs">Lista produków</span>
|
||||
</a>
|
||||
<? if ( $this -> level < 2 ):?>
|
||||
<a href="/admin/shop_category/category_edit/pid=<?= $category['id'];?>" title="Dodaj podkategorię" class="btn btn-sm btn-success">
|
||||
<i class="fa fa-plus-circle"></i> <span class="hidden-xs">Dodaj podkategorię</span>
|
||||
</a>
|
||||
<? endif;?>
|
||||
</div>
|
||||
</div>
|
||||
<?= \Tpl::view( 'shop-category/subcategories-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category['id'] ),
|
||||
'level' => $this -> level + 1,
|
||||
'dlang' => $this -> dlang
|
||||
] );?>
|
||||
</li>
|
||||
<? endforeach;?>
|
||||
</ol>
|
||||
<? endif;?>
|
||||
@@ -0,0 +1,25 @@
|
||||
<? if ( is_array( $this -> categories ) ):?>
|
||||
<ol>
|
||||
<? foreach ( $this -> categories as $category ):?>
|
||||
<li id="list_<?= $category['id'];?>" class="list_<?= $category['id'];?>" category="<?= $category['id'];?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<? if ( !$category['status'] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>';?>
|
||||
<b><?= $category['languages'][$this -> dlang]['title'];?></b>
|
||||
<div class="btn-group ml20 pull-right">
|
||||
<a href="#" title="Wybierz kategorię" class="btn btn-sm btn-system button category-select" category-title="<?= $category['languages'][$this -> dlang]['title'];?>" category-id="<?= $category['id'];?>">
|
||||
<i class="fa fa-check"></i> <span class="hidden-xs">wybierz</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?= \Tpl::view( 'shop-category/subcategory-browse-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category['id'] ),
|
||||
'level' => $this -> level + 1,
|
||||
'dlang' => $this -> dlang
|
||||
] );?>
|
||||
</li>
|
||||
<? endforeach;?>
|
||||
</ol>
|
||||
<? endif;?>
|
||||
@@ -0,0 +1,114 @@
|
||||
<style type="text/css">
|
||||
.layout-tree-toggle {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin-right: 4px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layout-tree-toggle:focus,
|
||||
.layout-tree-toggle:active,
|
||||
.layout-tree-toggle:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
li.sort-expanded > div .layout-tree-toggle i {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
#mass-edit-panel .product-item {
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
#mass-edit-panel .product-item label {
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#mass-edit-panel .content_menu .icheckbox_minimal-blue {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#mass-edit-panel .icheckbox_minimal-blue {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/minimal.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/minimal/blue.css">
|
||||
|
||||
<div class="panel mb50 panel-primary" id="mass-edit-panel">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title">Masowa edycja produktów</span>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<button class="btn btn-dark select-all">Zaznacz wszystkie</button>
|
||||
<button class="btn btn-default btn-outline-dark deselect-all">Odznacz wszystkie</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group mb10">
|
||||
<label class="col-lg-3 control-label" for="discount_percent">Ustaw cenę promocyjną (minus X procent)</label>
|
||||
<div class="col-lg-9">
|
||||
<div class="bs-component">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" id="discount_percent" name="discount_percent">
|
||||
<span class="input-group-addon cursor" field-id="discount_percent">
|
||||
<i class="fa fa-save"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<?php if ( is_array( $this->products ) ): foreach ( $this->products as $key => $product ): ?>
|
||||
<div class="product-item">
|
||||
<input type="checkbox" class="g-checkbox" name="products[]" id="product<?= $key; ?>" value="<?= $key; ?>">
|
||||
<label for="product<?= $key; ?>"><?= htmlspecialchars( $product ); ?></label>
|
||||
</div>
|
||||
<?php endforeach; endif; ?>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="menu_sortable">
|
||||
<ol class="sortable" id="sortable">
|
||||
<?php if ( is_array( $this->categories ) ): foreach ( $this->categories as $category ): ?>
|
||||
<li id="list_<?= $category['id']; ?>" class="category_<?= $category['id']; ?>" category="<?= $category['id']; ?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<?php if ( !$category['status'] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>'; ?>
|
||||
<input type="checkbox" class="g-checkbox" name="mass_categories[]" value="<?= $category['id']; ?>" />
|
||||
<b><?= $category['languages'][$this->dlang]['title']; ?></b>
|
||||
</div>
|
||||
<?= \Tpl::view( 'shop-product/subcategories-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category['id'] ),
|
||||
'level' => ($this->level ?? 0) + 1,
|
||||
'dlang' => $this->dlang,
|
||||
'name' => 'mass_categories[]'
|
||||
] ); ?>
|
||||
</li>
|
||||
<?php endforeach; endif; ?>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= \Tpl::view( 'shop-product/mass-edit-custom-script' ); ?>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
<? if ( is_array( $this -> categories ) ):?>
|
||||
<ol>
|
||||
<? foreach ( $this -> categories as $category ):?>
|
||||
<li id="list_<?= $category[ 'id' ];?>" class="list_<?= $category[ 'id' ];?>" category="<?= $category[ 'id' ];?>">
|
||||
<div class="context_0 content content_menu">
|
||||
<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</button>
|
||||
<? if ( !$category[ 'status' ] ) echo '<i class="fa fa-ban fa-lg text-danger" title="Kategoria nieaktywna"></i>';?>
|
||||
<input type="checkbox" class="g-checkbox" name="<?= $this -> name ? $this -> name : 'categories[]';?>" value="<?= $category[ 'id' ];?>" <? if ( is_array( $this -> product_categories ) and in_array( $category[ 'id' ], $this -> product_categories ) ):?>checked="checked"<? endif;?> />
|
||||
<b><?= $category[ 'languages' ][ $this -> dlang ][ 'title' ];?></b>
|
||||
</div>
|
||||
<?=
|
||||
\Tpl::view( 'shop-product/subcategories-list', [
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( $category[ 'id' ] ),
|
||||
'product_categories' => $this -> product_categories,
|
||||
'dlang' => $this -> dlang,
|
||||
'name' => $this -> name
|
||||
] );
|
||||
?>
|
||||
</li>
|
||||
<? endforeach;?>
|
||||
</ol>
|
||||
<? endif;?>
|
||||
273
temp/update_build/tmp_0.275/admin/templates/site/main-layout.php
Normal file
273
temp/update_build/tmp_0.275/admin/templates/site/main-layout.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<? global $user, $settings;?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>shopPro</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="author" content="www.project-pro.pl - internetowe rozwiązania dla biznesu">
|
||||
<link rel='stylesheet' type="text/css" href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700'>
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/skin/default_skin/css/theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/magnific/magnific-popup.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/datepicker/css/bootstrap-datetimepicker.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.structure.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.theme.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/framework/vendor/plugins/daterange/daterangepicker.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/easy-tabs/css/easy-responsive-tabs.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/bootstrap-4.5.2-dist/css/bootstrap.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/font-awesome-4.7.0/css/font-awesome.css">
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/grid/plugins/icheck/skins/square/blue.css">
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/jquery/jquery_ui/jquery-ui.min.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/js/utility/utility.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/magnific/jquery.magnific-popup.js"></script>
|
||||
<script type="text/javascript" src="/libraries/easy-tabs/js/easyResponsiveTabs.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/moment.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/moment/pl.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/datepicker/js/bootstrap-datetimepicker.js"></script>
|
||||
<script type="text/javascript" src="/libraries/framework/vendor/plugins/daterange/daterangepicker.js"></script>
|
||||
<script type="text/javascript" src="/libraries/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/libraries/bootstrap-4.5.2-dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script type="text/javascript" src="/libraries/grid/plugins/icheck/icheck.js"></script>
|
||||
<script type="text/javascript" src="/libraries/functions.js"></script>
|
||||
<script type="text/javascript" src="/admin/js/functions.js"></script>
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-page">
|
||||
<div class="menu">
|
||||
<div class="logo sticky-top">
|
||||
shop<b>Pro</b>
|
||||
<span>ver. <?= \S::get_version();?></span><br>
|
||||
<? if ( $settings[ 'update' ] and \S::get_new_version() > \S::get_version() ):?>
|
||||
<a href="/admin/update/main_view/" class="label label-danger">aktualizacja</a>
|
||||
<? endif;?>
|
||||
</div>
|
||||
<div class="menu-content">
|
||||
<ul>
|
||||
<li><a href="/admin/dashboard/main_view/"><img src="/admin/layout/icon/icon-menu/bxs-dashboard.svg">Pulpit</a></li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Sklep
|
||||
</div>
|
||||
<ul>
|
||||
<li> <a href="/admin/shop_order/view_list/"><img src="/admin/layout/icon/icon-menu/shopping-cart.svg">Zamówienia</a></li>
|
||||
<li> <a href="/admin/shop_clients/list/"><img src="/admin/layout/icon/icon-menu/people-fill.svg">Klienci</a></li>
|
||||
<li><a href="/admin/shop_category/list/"><img src="/admin/layout/icon/icon-menu/bxs-category-alt.svg">Kategorie</a></li>
|
||||
<li><a href="/admin/shop_product/view_list/"><img src="/admin/layout/icon/icon-menu/shopping-basket.svg">Produkty</a></li>
|
||||
<li><a href="/admin/shop_product/mass_edit/"><i class="fa fa-bars"></i>Masowa edycja</a></li>
|
||||
<li>
|
||||
<a href="/admin/shop_producer/list/">
|
||||
<i class="fa fa-user"></i>Producenci
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/shop_product_sets/list/">
|
||||
<i class="fa fa-bars"></i>Komplety Produktów
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="/admin/shop_attribute/list/"><img src="/admin/layout/icon/icon-menu/star-filled.svg">Cechy produktów</a></li>
|
||||
<li><a href="/admin/shop_transport/list/"><img src="/admin/layout/icon/icon-menu/bus.svg">Rodzaje transportu</a></li>
|
||||
<li><a href="/admin/shop_payment_method/list/"><img src="/admin/layout/icon/icon-menu/coins-fill.svg">Metody płatności</a></li>
|
||||
<li>
|
||||
<a href="/admin/shop_statuses/list/"><i class="fa fa-bars"></i>Statusy zamówień</a>
|
||||
</li>
|
||||
<li><a href="/admin/shop_coupon/list/"><img src="/admin/layout/icon/icon-menu/piggy-bank-coins.svg">Kody rabatowe</a></li>
|
||||
<li><a href="/admin/shop_promotion/list/"><img src="/admin/layout/icon/icon-menu/burst-sale.svg">Promocje</a></li>
|
||||
</ul>
|
||||
<div class="title">Zawartość</div>
|
||||
<ul>
|
||||
<li><a href="/admin/pages/list/"><img src="/admin/layout/icon/icon-menu/menu(1).svg">Konfiguracja menu</a></li>
|
||||
<li><a href="/admin/articles/list/"><img src="/admin/layout/icon/icon-menu/note.svg">Artykuły</a></li>
|
||||
<li><a href="/admin/articles_archive/list/"><img src="/admin/layout/icon/icon-menu/baseline-sticky-note-2.svg">Archiwum artykułów</a></li>
|
||||
<li><a href="/admin/scontainers/list/"><img src="/admin/layout/icon/icon-menu/view-cols.svg">Zawartość statyczna</a></li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Newsletter
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/admin/newsletter/emails_list/"><img src="/admin/layout/icon/icon-menu/bx-at.svg">Baza emaili</a></li>
|
||||
<li><a href="/admin/newsletter/email_templates_admin/"><img src="/admin/layout/icon/icon-menu/template.svg">Szablony administracyjne</a></li>
|
||||
<li><a href="/admin/newsletter/settings/"><img src="/admin/layout/icon/icon-menu/baseline-settings.svg">Ustawienia</a></li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Pozostałe
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/admin/filemanager/draw/"><img src="/admin/layout/icon/icon-menu/file-image-outlined.svg">Menadżer plików</a></li>
|
||||
<li><a href="/admin/banners/list/"><img src="/admin/layout/icon/icon-menu/photo-video.svg">Banery</a></li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Konfiguracja
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/admin/users/view_list/"><img src="/admin/layout/icon/icon-menu/people-fill.svg">Użytkownicy</a></li>
|
||||
<li><a href="/admin/languages/view_list/"><img src="/admin/layout/icon/icon-menu/letter-aa.svg">Języki</a></li>
|
||||
<li><a href="/admin/languages/translation_list/"><img src="/admin/layout/icon/icon-menu/language.svg">Tłumaczenia</a></li>
|
||||
<li><a href="/admin/settings/view/"><img src="/admin/layout/icon/icon-menu/baseline-settings.svg">Ustawienia</a></li>
|
||||
<li><a href="/admin/layouts/list/"><img src="/admin/layout/icon/icon-menu/calendar-settings.svg">Szablony</a></li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Słowniki
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/dictionaries/list/">
|
||||
<i class="fa fa-puzzle-piece" aria-hidden="true"></i>Jednostki miary
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Archiwum
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/product_archive/list/">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>Produkty
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="title">
|
||||
Integracje
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/integrations/apilo_settings/">
|
||||
<i class="fa fa-cogs" aria-hidden="true"></i>apilo.com
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/integrations/shoppro_settings/">
|
||||
<i class="fa fa-cogs" aria-hidden="true"></i>shopPRO
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<a href="/" target="_blank"><img src="/admin/layout/icon/icon-menu/chevrons-right.svg">Podgląd</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-menu-btn" id="mobile-menu-btn">
|
||||
<i class="fa fa-bars"></i>
|
||||
</div>
|
||||
<div class="site-content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-3 col-lg-2">
|
||||
<button id="clear-cache-btn" class="btn btn-danger mt-3">Wyczyść cache</button>
|
||||
</div>
|
||||
<div class="col-12 col-md-9 col-lg-10 top-user">
|
||||
<div class="dropdown">
|
||||
<?
|
||||
if ( $user[ 'name' ] or $user[ 'surname' ] )
|
||||
echo $user[ 'surname' ] . ' ' . $user[ 'name' ];
|
||||
else
|
||||
echo $user[ 'login' ];
|
||||
?>
|
||||
<img src="/admin/layout/icon/user-avatar-filled.svg" alt="avatar" >
|
||||
<div class="dropdown-content">
|
||||
<a href="/admin/?s-action=user-logout"><span class="fa fa-power-off"></span>Wyloguj się</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?
|
||||
if ( $alert = \S::get_session( 'alert' ) ):
|
||||
\S::alert( false );
|
||||
?>
|
||||
<div class="row">
|
||||
<div class=" col-12">
|
||||
<div class="alert alert-danger alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="fa fa-info pr10"></i>
|
||||
<?= $alert;?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<? endif;?>
|
||||
<div class="row">
|
||||
<div class="col-12 dashboard-page" id="content">
|
||||
<?= $this -> content;?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script class="footer" type="text/javascript">
|
||||
(function() {
|
||||
function bindClearCacheButton() {
|
||||
$(document)
|
||||
.off('click.clearCacheBtn', '#clear-cache-btn')
|
||||
.on('click.clearCacheBtn', '#clear-cache-btn', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $btn = $(this);
|
||||
var originalText = $btn.text();
|
||||
|
||||
// Show pending state
|
||||
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Czyszcz\u0119 cache...');
|
||||
|
||||
// Send AJAX request
|
||||
$.ajax({
|
||||
url: '/admin/settings/clearCacheAjax/',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
// Show success state
|
||||
$btn.html('<i class="fa fa-check"></i> Cache wyczyszczony!').removeClass('btn-danger').addClass('btn-success');
|
||||
|
||||
// Restore original state after delay
|
||||
setTimeout(function() {
|
||||
$btn.prop('disabled', false).html(originalText).removeClass('btn-success').addClass('btn-danger');
|
||||
}, 2000);
|
||||
} else {
|
||||
// Request failed on server
|
||||
$btn.html('<i class="fa fa-exclamation-triangle"></i> B\u0142\u0105d!').removeClass('btn-danger').addClass('btn-warning');
|
||||
setTimeout(function() {
|
||||
$btn.prop('disabled', false).html(originalText).removeClass('btn-warning').addClass('btn-danger');
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
// Network or transport error
|
||||
$btn.html('<i class="fa fa-exclamation-triangle"></i> B\u0142\u0105d po\u0142\u0105czenia!').removeClass('btn-danger').addClass('btn-warning');
|
||||
setTimeout(function() {
|
||||
$btn.prop('disabled', false).html(originalText).removeClass('btn-warning').addClass('btn-danger');
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bindClearCacheButton();
|
||||
})();
|
||||
|
||||
$(document).ready(function () {
|
||||
var user_agent = navigator.userAgent.toLowerCase();
|
||||
var click_event = user_agent.match(/(iphone|ipod|ipad)/) ? "touchend" : "click";
|
||||
$('body').on(click_event, '#mobile-menu-btn', function ()
|
||||
{
|
||||
if ($(this).hasClass('active'))
|
||||
{
|
||||
$(this).removeClass('active');
|
||||
$( '.menu' ).removeClass( 'visible' );
|
||||
$( '#mobile-menu-btn i' ).addClass( 'fa-bars' ).removeClass( 'fa-times' );
|
||||
$( '.site-content' ).removeClass( 'with-menu' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).addClass('active');
|
||||
$( '.menu' ).addClass( 'visible' );
|
||||
$( '.site-content' ).addClass( 'with-menu' );
|
||||
$( '#mobile-menu-btn i' ).addClass( 'fa-times' ).removeClass( 'fa-bars' );
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Category\CategoryRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
|
||||
class ShopCategoryController
|
||||
{
|
||||
private CategoryRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
|
||||
public function __construct(CategoryRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return \Tpl::view('shop-category/categories-list', [
|
||||
'categories' => $this->repository->subcategories(null),
|
||||
'level' => 0,
|
||||
'dlang' => \front\factory\Languages::default_language(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return $this->view_list();
|
||||
}
|
||||
|
||||
public function category_edit(): string
|
||||
{
|
||||
return \Tpl::view('shop-category/category-edit', [
|
||||
'category' => $this->repository->categoryDetails(\S::get('id')),
|
||||
'pid' => \S::get('pid'),
|
||||
'languages' => $this->languagesRepository->languagesList(),
|
||||
'sort_types' => $this->repository->sortTypes(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
return $this->category_edit();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania kategorii wystąpił błąd. Proszę spróbować ponownie.',
|
||||
];
|
||||
|
||||
$values = json_decode((string)\S::get('values'), true);
|
||||
if (is_array($values)) {
|
||||
$savedId = $this->repository->save($values);
|
||||
if (!empty($savedId)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Kategoria została zapisana.',
|
||||
'id' => (int)$savedId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function category_delete(): void
|
||||
{
|
||||
if ($this->repository->categoryDelete(\S::get('id'))) {
|
||||
\S::set_message('Kategoria została usunięta.');
|
||||
} else {
|
||||
\S::alert('Podczas usuwania kategorii wystąpił błąd. Aby usunąć kategorię nie może ona posiadać przypiętych podkategorii.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_category/view_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
$this->category_delete();
|
||||
}
|
||||
|
||||
public function category_products(): string
|
||||
{
|
||||
return \Tpl::view('shop-category/category-products', [
|
||||
'category_id' => \S::get('id'),
|
||||
'products' => $this->repository->categoryProducts((int)\S::get('id')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function products(): string
|
||||
{
|
||||
return $this->category_products();
|
||||
}
|
||||
|
||||
public function category_url_browser(): void
|
||||
{
|
||||
echo \Tpl::view('shop-category/category-browse-list', [
|
||||
'categories' => $this->repository->subcategories(null),
|
||||
'level' => 0,
|
||||
'dlang' => \front\factory\Languages::default_language(),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function save_categories_order(): void
|
||||
{
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania kolejności kategorii wystąpił błąd. Proszę spróbować ponownie.',
|
||||
];
|
||||
|
||||
if ( $this->repository->saveCategoriesOrder( \S::get( 'categories' ) ) ) {
|
||||
$response = [ 'status' => 'ok' ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function save_products_order(): void
|
||||
{
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania kolejności wyświetlania produktów wystąpił błąd. Proszę spróbować ponownie.',
|
||||
];
|
||||
|
||||
if ( $this->repository->saveProductOrder( \S::get( 'category_id' ), \S::get( 'products' ) ) ) {
|
||||
$response = [ 'status' => 'ok' ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function cookie_categories(): void
|
||||
{
|
||||
$categoryId = (string) \S::get( 'category_id' );
|
||||
if ( $categoryId === '' ) {
|
||||
echo json_encode( [ 'status' => 'error' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$array = [];
|
||||
if ( isset( $_COOKIE['cookie_categories'] ) ) {
|
||||
$tmp = @unserialize( (string) $_COOKIE['cookie_categories'] );
|
||||
if ( is_array( $tmp ) ) {
|
||||
$array = $tmp;
|
||||
}
|
||||
}
|
||||
|
||||
$array[$categoryId] = isset( $array[$categoryId] ) && (int) $array[$categoryId] === 1 ? 0 : 1;
|
||||
|
||||
setcookie( 'cookie_categories', serialize( $array ), time() + 3600 * 24 * 365, '/' );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Product\ProductRepository;
|
||||
use Domain\Category\CategoryRepository;
|
||||
|
||||
/**
|
||||
* Kontroler masowej edycji produktów.
|
||||
* Obsługuje akcje: mass_edit (widok), mass_edit_save (AJAX), get_products_by_category (AJAX).
|
||||
* Pozostałe akcje shop_product (view_list, product_edit, save itd.) nadal działają
|
||||
* przez fallback na \admin\controls\ShopProduct.
|
||||
*/
|
||||
class ShopProductController
|
||||
{
|
||||
private ProductRepository $repository;
|
||||
|
||||
public function __construct(ProductRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widok masowej edycji produktów.
|
||||
*/
|
||||
public function mass_edit(): string
|
||||
{
|
||||
$categoryRepository = new CategoryRepository( $GLOBALS['mdb'] );
|
||||
|
||||
return \Tpl::view( 'shop-product/mass-edit', [
|
||||
'products' => $this->repository->allProductsForMassEdit(),
|
||||
'categories' => $categoryRepository->subcategories( null ),
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: zastosowanie rabatu procentowego na zaznaczonych produktach.
|
||||
*/
|
||||
public function mass_edit_save(): void
|
||||
{
|
||||
$discountPercent = \S::get( 'discount_percent' );
|
||||
$products = \S::get( 'products' );
|
||||
|
||||
if ( $discountPercent != '' && $products && is_array( $products ) && count( $products ) > 0 ) {
|
||||
$productId = (int) $products[0];
|
||||
$result = $this->repository->applyDiscountPercent( $productId, (float) $discountPercent );
|
||||
|
||||
if ( $result !== null ) {
|
||||
echo json_encode( [
|
||||
'status' => 'ok',
|
||||
'price_brutto_promo' => $result['price_brutto_promo'],
|
||||
'price_brutto' => $result['price_brutto']
|
||||
] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode( [ 'status' => 'error' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: pobranie ID produktów z danej kategorii.
|
||||
*/
|
||||
public function get_products_by_category(): void
|
||||
{
|
||||
$categoryId = (int) \S::get( 'category_id' );
|
||||
$products = $this->repository->getProductsByCategory( $categoryId );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok', 'products' => $products ] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
493
temp/update_build/tmp_0.275/autoload/admin/class.Site.php
Normal file
493
temp/update_build/tmp_0.275/autoload/admin/class.Site.php
Normal file
@@ -0,0 +1,493 @@
|
||||
<?php
|
||||
namespace admin;
|
||||
|
||||
class Site
|
||||
{
|
||||
// define APP_SECRET_KEY
|
||||
const APP_SECRET_KEY = 'c3cb2537d25c0efc9e573d059d79c3b8';
|
||||
|
||||
static public function finalize_admin_login( array $user, string $domain, string $cookie_name, bool $remember = false ) {
|
||||
|
||||
\S::set_session( 'user', $user );
|
||||
\S::delete_session( 'twofa_pending' );
|
||||
|
||||
if ( $remember ) {
|
||||
$payloadArr = [
|
||||
'login' => $user['login'],
|
||||
'ts' => time()
|
||||
];
|
||||
|
||||
$json = json_encode($payloadArr, JSON_UNESCAPED_SLASHES);
|
||||
$sig = hash_hmac('sha256', $json, self::APP_SECRET_KEY);
|
||||
$payload = base64_encode($json . '.' . $sig);
|
||||
|
||||
setcookie( $cookie_name, $payload, [
|
||||
'expires' => time() + (86400 * 14),
|
||||
'path' => '/',
|
||||
'domain' => $domain,
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public static function special_actions()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$sa = \S::get('s-action');
|
||||
$domain = preg_replace('/^www\./', '', $_SERVER['SERVER_NAME']);
|
||||
$cookie_name = 'admin_remember_' . str_replace( '.', '-', $domain );
|
||||
$users = new \Domain\User\UserRepository($mdb);
|
||||
|
||||
switch ($sa)
|
||||
{
|
||||
case 'user-logon':
|
||||
{
|
||||
$login = \S::get('login');
|
||||
$pass = \S::get('password');
|
||||
|
||||
$result = $users->logon($login, $pass);
|
||||
|
||||
if ( $result == 1 )
|
||||
{
|
||||
$user = $users->details($login);
|
||||
|
||||
if ( $user['twofa_enabled'] == 1 )
|
||||
{
|
||||
\S::set_session( 'twofa_pending', [
|
||||
'uid' => (int)$user['id'],
|
||||
'login' => $login,
|
||||
'remember' => (bool)\S::get('remember'),
|
||||
'started' => time(),
|
||||
] );
|
||||
|
||||
if ( !$users->sendTwofaCode( (int)$user['id'] ) )
|
||||
{
|
||||
\S::alert('Nie udało się wysłać kodu 2FA. Spróbuj ponownie.');
|
||||
\S::delete_session('twofa_pending');
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Location: /admin/user/twofa/');
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
$user = $users->details($login);
|
||||
|
||||
self::finalize_admin_login(
|
||||
$user,
|
||||
$domain,
|
||||
$cookie_name,
|
||||
(bool)\S::get('remember')
|
||||
);
|
||||
|
||||
header('Location: /admin/articles/list/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($result == -1)
|
||||
{
|
||||
\S::alert('Z powodu 5 nieudanych prób Twoje konto zostało zablokowane.');
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert('Podane hasło jest nieprawidłowe lub użytkownik nie istnieje.');
|
||||
}
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'user-2fa-verify':
|
||||
{
|
||||
$pending = \S::get_session('twofa_pending');
|
||||
if ( !$pending || empty( $pending['uid'] ) ) {
|
||||
\S::alert('Sesja 2FA wygasła. Zaloguj się ponownie.');
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$code = trim((string)\S::get('twofa'));
|
||||
if (!preg_match('/^\d{6}$/', $code))
|
||||
{
|
||||
\S::alert('Nieprawidłowy format kodu.');
|
||||
header('Location: /admin/user/twofa/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$ok = $users->verifyTwofaCode((int)$pending['uid'], $code);
|
||||
if (!$ok)
|
||||
{
|
||||
\S::alert('Błędny lub wygasły kod.');
|
||||
header('Location: /admin/user/twofa/');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2FA OK - finalna sesja
|
||||
$user = $users->details($pending['login']);
|
||||
|
||||
self::finalize_admin_login(
|
||||
$user,
|
||||
$domain,
|
||||
$cookie_name,
|
||||
$pending['remember'] ? true : false
|
||||
);
|
||||
|
||||
header('Location: /admin/articles/list/');
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'user-2fa-resend':
|
||||
{
|
||||
$pending = \S::get_session('twofa_pending');
|
||||
if (!$pending || empty($pending['uid']))
|
||||
{
|
||||
\S::alert('Sesja 2FA wygasła. Zaloguj się ponownie.');
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$users->sendTwofaCode((int)$pending['uid'], true))
|
||||
{
|
||||
\S::alert('Kod można wysłać ponownie po krótkiej przerwie.');
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert('Nowy kod został wysłany.');
|
||||
}
|
||||
header('Location: /admin/user/twofa/');
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'user-logout':
|
||||
{
|
||||
setcookie($cookie_name, "", [
|
||||
'expires' => time() - 86400,
|
||||
'path' => '/',
|
||||
'domain' => $domain,
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
\S::delete_session('twofa_pending');
|
||||
session_destroy();
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapa nowych kontrolerów: module => fabryka kontrolera (DI)
|
||||
* Przy migracji kolejnego kontrolera - dodaj wpis tutaj
|
||||
*/
|
||||
private static $newControllers = [];
|
||||
|
||||
/**
|
||||
* Zwraca mapę fabryk kontrolerów (inicjalizacja runtime)
|
||||
*/
|
||||
private static function getControllerFactories(): array
|
||||
{
|
||||
if ( !empty( self::$newControllers ) )
|
||||
return self::$newControllers;
|
||||
|
||||
self::$newControllers = [
|
||||
'Articles' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ArticlesController(
|
||||
new \Domain\Article\ArticleRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb ),
|
||||
new \Domain\Layouts\LayoutsRepository( $mdb ),
|
||||
new \Domain\Pages\PagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ArticlesArchive' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ArticlesArchiveController(
|
||||
new \Domain\Article\ArticleRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Banners' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\BannerController(
|
||||
new \Domain\Banner\BannerRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Settings' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\SettingsController(
|
||||
new \Domain\Settings\SettingsRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ProductArchive' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ProductArchiveController(
|
||||
new \Domain\Product\ProductRepository( $mdb )
|
||||
);
|
||||
},
|
||||
// Alias dla starego modułu /admin/archive/list/
|
||||
'Archive' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ProductArchiveController(
|
||||
new \Domain\Product\ProductRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Dictionaries' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\DictionariesController(
|
||||
new \Domain\Dictionaries\DictionariesRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Filemanager' => function() {
|
||||
return new \admin\Controllers\FilemanagerController();
|
||||
},
|
||||
'Users' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\UsersController(
|
||||
new \Domain\User\UserRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Languages' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\LanguagesController(
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Layouts' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\LayoutsController(
|
||||
new \Domain\Layouts\LayoutsRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Newsletter' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\NewsletterController(
|
||||
new \Domain\Newsletter\NewsletterRepository(
|
||||
$mdb,
|
||||
new \Domain\Settings\SettingsRepository( $mdb )
|
||||
),
|
||||
new \Domain\Newsletter\NewsletterPreviewRenderer()
|
||||
);
|
||||
},
|
||||
'Scontainers' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ScontainersController(
|
||||
new \Domain\Scontainers\ScontainersRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopPromotion' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopPromotionController(
|
||||
new \Domain\Promotion\PromotionRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopCoupon' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopCouponController(
|
||||
new \Domain\Coupon\CouponRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopAttribute' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopAttributeController(
|
||||
new \Domain\Attribute\AttributeRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopPaymentMethod' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopPaymentMethodController(
|
||||
new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopTransport' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopTransportController(
|
||||
new \Domain\Transport\TransportRepository( $mdb ),
|
||||
new \Domain\PaymentMethod\PaymentMethodRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Pages' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\PagesController(
|
||||
new \Domain\Pages\PagesRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb ),
|
||||
new \Domain\Layouts\LayoutsRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'Integrations' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\IntegrationsController(
|
||||
new \Domain\Integrations\IntegrationsRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopStatuses' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopStatusesController(
|
||||
new \Domain\ShopStatus\ShopStatusRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopProductSets' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopProductSetsController(
|
||||
new \Domain\ProductSet\ProductSetRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopProducer' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopProducerController(
|
||||
new \Domain\Producer\ProducerRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopCategory' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopCategoryController(
|
||||
new \Domain\Category\CategoryRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopProduct' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopProductController(
|
||||
new \Domain\Product\ProductRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopClients' => function() {
|
||||
global $mdb;
|
||||
|
||||
return new \admin\Controllers\ShopClientsController(
|
||||
new \Domain\Client\ClientRepository( $mdb )
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
return self::$newControllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy instancję nowego kontrolera z Dependency Injection
|
||||
*/
|
||||
private static function createController( string $moduleName )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$factories = self::getControllerFactories();
|
||||
if ( !isset( $factories[$moduleName] ) )
|
||||
return null;
|
||||
|
||||
$factory = $factories[$moduleName];
|
||||
if ( !is_callable( $factory ) )
|
||||
return null;
|
||||
|
||||
return $factory();
|
||||
}
|
||||
|
||||
|
||||
public static function route()
|
||||
{
|
||||
$_SESSION['admin'] = true;
|
||||
|
||||
if ( \S::get( 'p' ) )
|
||||
\S::set_session( 'p' , \S::get( 'p' ) );
|
||||
|
||||
$page = \S::get_session( 'p' );
|
||||
|
||||
// Budowanie nazwy modułu
|
||||
$moduleName = '';
|
||||
$results = explode( '_', \S::get( 'module' ) );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$moduleName .= ucfirst( $row );
|
||||
|
||||
$action = \S::get( 'action' );
|
||||
|
||||
// 1. Sprawdź czy istnieje nowy kontroler
|
||||
$factories = self::getControllerFactories();
|
||||
if ( isset( $factories[$moduleName] ) )
|
||||
{
|
||||
$controller = self::createController( $moduleName );
|
||||
if ( $controller )
|
||||
{
|
||||
if ( method_exists( $controller, $action ) )
|
||||
{
|
||||
return $controller->$action();
|
||||
}
|
||||
|
||||
if ( $moduleName === 'ShopAttribute' )
|
||||
{
|
||||
\S::alert( 'Nieprawidłowy adres url.' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 2. Fallback na stary kontroler
|
||||
$class = '\admin\controls\\' . $moduleName;
|
||||
|
||||
if ( class_exists( $class ) and method_exists( new $class, $action ) )
|
||||
return call_user_func_array( array( $class, $action ), array() );
|
||||
else
|
||||
{
|
||||
\S::alert( 'Nieprawidłowy adres url.' );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static public function update()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( $results = $mdb -> select( 'pp_updates', [ 'name' ], [ 'done' => 0 ] ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$class = '\admin\factory\Update';
|
||||
$method = $row['name'];
|
||||
|
||||
if ( class_exists( $class ) and method_exists( new $class, $method ) )
|
||||
call_user_func_array( array( $class, $method ), array() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopProduct
|
||||
{
|
||||
static public function generate_combination()
|
||||
{
|
||||
foreach ( $_POST as $key => $val )
|
||||
{
|
||||
if ( strpos( $key, 'attribute_' ) !== false )
|
||||
{
|
||||
$attribute = explode( 'attribute_', $key );
|
||||
$attributes[ $attribute[1] ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \admin\factory\ShopProduct::generate_permutation( (int) \S::get( 'product_id' ), $attributes ) )
|
||||
\S::alert( 'Kombinacje produktu zostały wygenerowane.' );
|
||||
|
||||
header( 'Location: /admin/shop_product/product_combination/product_id=' . (int) \S::get( 'product_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
//usunięcie kombinacji produktu
|
||||
static public function delete_combination()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::delete_combination( (int)\S::get( 'combination_id' ) ) )
|
||||
\S::alert( 'Kombinacja produktu została usunięta' );
|
||||
else
|
||||
\S::alert( 'Podczas usuwania kombinacji produktu wystąpił błąd. Proszę spróbować ponownie' );
|
||||
|
||||
header( 'Location: /admin/shop_product/product_combination/product_id=' . \S::get( 'product_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function duplicate_product()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::duplicate_product( (int)\S::get( 'product-id' ), (int)\S::get( 'combination' ) ) )
|
||||
\S::set_message( 'Produkt został zduplikowany.' );
|
||||
else
|
||||
\S::alert( 'Podczas duplikowania produktu wystąpił błąd. Proszę spróbować ponownie' );
|
||||
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function image_delete()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas usuwania zdjecia wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::delete_img( \S::get( 'image_id' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function images_order_save()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::images_order_save( \S::get( 'product_id' ), \S::get( 'order' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok', 'msg' => 'Produkt został zapisany.' ] );
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function image_alt_change()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany atrybutu alt zdjęcia wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::image_alt_change( \S::get( 'image_id' ), \S::get( 'image_alt' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// szybka zmiana statusu produktu
|
||||
static public function change_product_status() {
|
||||
|
||||
if ( \admin\factory\ShopProduct::change_product_status( (int)\S::get( 'product-id' ) ) )
|
||||
\S::set_message( 'Status produktu został zmieniony' );
|
||||
|
||||
header( 'Location: ' . $_SERVER['HTTP_REFERER'] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// szybka zmiana google xml label
|
||||
static public function product_change_custom_label()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany google xml label wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::product_change_custom_label( (int) \S::get( 'product_id' ), \S::get( 'custom_label' ), \S::get( 'value' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// szybka zmiana ceny promocyjnej
|
||||
static public function product_change_price_brutto_promo()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany ceny wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::product_change_price_brutto_promo( (int) \S::get( 'product_id' ), \S::get( 'price' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// szybka zmiana ceny
|
||||
static public function product_change_price_brutto()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zmiany ceny wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \admin\factory\ShopProduct::product_change_price_brutto( (int) \S::get( 'product_id' ), \S::get( 'price' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// pobierz bezpośredni url produktu
|
||||
static public function ajax_product_url()
|
||||
{
|
||||
echo json_encode( [ 'url' => \shop\Product::getProductUrl( \S::get( 'product_id' ) ) ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// zapisanie produktu
|
||||
public static function save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania produktu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\ShopProduct::save(
|
||||
$values['id'], $values['name'], $values['short_description'], $values['description'], $values['status'], $values['meta_description'], $values['meta_keywords'], $values['seo_link'],
|
||||
$values['copy_from'], $values['categories'], $values['price_netto'], $values['price_brutto'], $values['vat'], $values['promoted'], $values['warehouse_message_zero'], $values['warehouse_message_nonzero'], $values['tab_name_1'],
|
||||
$values['tab_description_1'], $values['tab_name_2'], $values['tab_description_2'], $values['layout_id'], $values['products_related'], (int) $values['set'], $values['price_netto_promo'], $values['price_brutto_promo'],
|
||||
$values['new_to_date'], $values['stock_0_buy'], $values['wp'], $values['custom_label_0'], $values['custom_label_1'], $values['custom_label_2'], $values['custom_label_3'], $values['custom_label_4'], $values['additional_message'], (int)$values['quantity'], $values['additional_message_text'], $values['additional_message_required'] == 'on' ? 1 : 0, $values['canonical'], $values['meta_title'], $values['producer_id'], $values['sku'], $values['ean'], $values['product_unit'], $values['weight'], $values['xml_name'], $values['custom_field_name'], $values['custom_field_required'], $values['security_information'], $values['custom_field_type']
|
||||
) ) {
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Produkt został zapisany.', 'id' => $id ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// product_unarchive
|
||||
static public function product_unarchive()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::product_unarchive( (int) \S::get( 'product_id' ) ) )
|
||||
\S::alert( 'Produkt został przywrócony z archiwum.' );
|
||||
else
|
||||
\S::alert( 'Podczas przywracania produktu z archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
||||
|
||||
header( 'Location: /admin/product_archive/list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function product_archive()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::product_archive( (int) \S::get( 'product_id' ) ) )
|
||||
\S::alert( 'Produkt został przeniesiony do archiwum.' );
|
||||
else
|
||||
\S::alert( 'Podczas przenoszenia produktu do archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
||||
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function product_delete()
|
||||
{
|
||||
if ( \admin\factory\ShopProduct::product_delete( (int) \S::get( 'id' ) ) )
|
||||
\S::set_message( 'Produkt został usunięty.' );
|
||||
else
|
||||
\S::alert( 'Podczas usuwania produktu wystąpił błąd. Proszę spróbować ponownie' );
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// edycja produktu
|
||||
public static function product_edit() {
|
||||
global $user, $mdb;
|
||||
|
||||
if ( !$user ) {
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
\admin\factory\ShopProduct::delete_nonassigned_images();
|
||||
\admin\factory\ShopProduct::delete_nonassigned_files();
|
||||
|
||||
return \Tpl::view( 'shop-product/product-edit', [
|
||||
'product' => \admin\factory\ShopProduct::product_details( (int) \S::get( 'id' ) ),
|
||||
'languages' => ( new \Domain\Languages\LanguagesRepository( $GLOBALS['mdb'] ) )->languagesList(),
|
||||
'categories' => ( new \Domain\Category\CategoryRepository( $GLOBALS['mdb'] ) )->subcategories( null ),
|
||||
'layouts' => self::layouts_for_product_edit( $mdb ),
|
||||
'products' => \admin\factory\ShopProduct::products_list(),
|
||||
'dlang' => \front\factory\Languages::default_language(),
|
||||
'sets' => \shop\ProductSet::sets_list(),
|
||||
'producers' => ( new \Domain\Producer\ProducerRepository( $mdb ) )->allProducers(),
|
||||
'units' => ( new \Domain\Dictionaries\DictionariesRepository( $mdb ) ) -> allUnits(),
|
||||
'user' => $user
|
||||
] );
|
||||
}
|
||||
|
||||
private static function layouts_for_product_edit( $db )
|
||||
{
|
||||
if ( class_exists( '\Domain\Layouts\LayoutsRepository' ) )
|
||||
{
|
||||
$rows = ( new \Domain\Layouts\LayoutsRepository( $db ) ) -> listAll();
|
||||
return is_array( $rows ) ? $rows : [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// ajax_load_products ARCHIVE
|
||||
static public function ajax_load_products_archive()
|
||||
{
|
||||
echo json_encode( [
|
||||
'status' => 'deprecated',
|
||||
'msg' => 'Endpoint nie jest juz wspierany. Uzyj /admin/product_archive/list/.',
|
||||
'redirect_url' => '/admin/product_archive/list/'
|
||||
] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// ajax_load_products
|
||||
static public function ajax_load_products() {
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas ładowania produktów wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
\S::set_session( 'products_list_current_page', \S::get( 'current_page' ) );
|
||||
\S::set_session( 'products_list_query', \S::get( 'query' ) );
|
||||
|
||||
if ( $products = \admin\factory\ShopProduct::ajax_products_list( \S::get_session( 'products_list_current_page' ), \S::get_session( 'products_list_query' ) ) ) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'pagination_max' => ceil( $products['products_count'] / 10 ),
|
||||
'html' => \Tpl::view( 'shop-product/products-list-table', [
|
||||
'products' => $products['products'],
|
||||
'current_page' => \S::get( 'current_page' ),
|
||||
'apilo_enabled' => \admin\factory\Integrations::apilo_settings( 'enabled' ),
|
||||
'show_xml_data' => \S::get_session( 'show_xml_data' )
|
||||
] )
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function view_list()
|
||||
{
|
||||
$current_page = \S::get_session( 'products_list_current_page' );
|
||||
|
||||
if ( !$current_page ) {
|
||||
$current_page = 1;
|
||||
\S::set_session( 'products_list_current_page', $current_page );
|
||||
}
|
||||
|
||||
$query = \S::get_session( 'products_list_query' );
|
||||
if ( $query ) {
|
||||
$query_array = [];
|
||||
parse_str( $query, $query_array );
|
||||
}
|
||||
|
||||
if ( \S::get( 'show_xml_data' ) === 'true' ) {
|
||||
\S::set_session( 'show_xml_data', true );
|
||||
} else if ( \S::get( 'show_xml_data' ) === 'false' ) {
|
||||
\S::set_session( 'show_xml_data', false );
|
||||
}
|
||||
|
||||
return \Tpl::view( 'shop-product/products-list', [
|
||||
'current_page' => $current_page,
|
||||
'query_array' => $query_array,
|
||||
'pagination_max' => ceil( \admin\factory\ShopProduct::count_product() / 10 ),
|
||||
'apilo_enabled' => \admin\factory\Integrations::apilo_settings( 'enabled' ),
|
||||
'show_xml_data' => \S::get_session( 'show_xml_data' ),
|
||||
'shoppro_enabled' => \admin\factory\Integrations::shoppro_settings( 'enabled' )
|
||||
] );
|
||||
}
|
||||
|
||||
//
|
||||
// KOMBINACJE PRODUKTU
|
||||
//
|
||||
|
||||
// zapisanie możliwości zakupu przy stanie 0 w kombinacji produktu
|
||||
static public function product_combination_stock_0_buy_save()
|
||||
{
|
||||
\admin\factory\ShopProduct::product_combination_stock_0_buy_save( (int)\S::get( 'product_id' ), \S::get( 'stock_0_buy' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// zapisanie sku w kombinacji produktu
|
||||
static public function product_combination_sku_save()
|
||||
{
|
||||
\admin\factory\ShopProduct::product_combination_sku_save( (int)\S::get( 'product_id' ), \S::get( 'sku' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// zapisanie ilości w kombinacji produktu
|
||||
static public function product_combination_quantity_save()
|
||||
{
|
||||
\admin\factory\ShopProduct::product_combination_quantity_save( (int)\S::get( 'product_id' ), \S::get( 'quantity' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// zapisanie ceny w kombinacji produktu
|
||||
static public function product_combination_price_save()
|
||||
{
|
||||
\admin\factory\ShopProduct::product_combination_price_save( (int)\S::get( 'product_id' ), \S::get( 'price' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
//wyświetlenie kombinacji produktu
|
||||
static public function product_combination()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
return \Tpl::view( 'shop-product/product-combination', [
|
||||
'product' => \admin\factory\ShopProduct::product_details( (int) \S::get( 'product_id' ) ),
|
||||
'attributes' => ( new \Domain\Attribute\AttributeRepository( $mdb ) ) -> getAttributesListForCombinations(),
|
||||
'default_language' => \front\factory\Languages::default_language(),
|
||||
'product_permutations' => \admin\factory\ShopProduct::get_product_permutations( (int) \S::get( 'product_id' ) )
|
||||
] );
|
||||
}
|
||||
|
||||
// generate_sku_code
|
||||
static public function generate_sku_code() {
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas generowania kodu sku wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( $sku = \shop\Product::generate_sku_code( \S::get( 'product_id' ) ) )
|
||||
$response = [ 'status' => 'ok', 'sku' => $sku ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// product_xml_name_save
|
||||
static public function product_xml_name_save() {
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania nazwy produktu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \shop\Product::product_xml_name_save( \S::get( 'product_id' ), \S::get( 'product_xml_name' ), \S::get( 'lang_id' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// product_custom_label_suggestions
|
||||
static public function product_custom_label_suggestions() {
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas pobierania sugestii dla custom label wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( $suggestions = \shop\Product::product_custom_label_suggestions( \S::get( 'custom_label' ), \S::get( 'label_type' ) ) )
|
||||
$response = [ 'status' => 'ok', 'suggestions' => $suggestions ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// product_custom_label_save
|
||||
static public function product_custom_label_save() {
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania custom label wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
|
||||
if ( \shop\Product::product_custom_label_save( \S::get( 'product_id' ), \S::get( 'custom_label' ), \S::get( 'label_type' ) ) )
|
||||
$response = [ 'status' => 'ok' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
458
temp/update_build/tmp_0.275/docs/CHANGELOG.md
Normal file
458
temp/update_build/tmp_0.275/docs/CHANGELOG.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# Changelog shopPRO
|
||||
|
||||
Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.275 (2026-02-15) - ShopCategory
|
||||
|
||||
- **ShopCategory** - migracja `/admin/shop_category/*` na Domain + DI + nowe endpointy AJAX
|
||||
- NOWE: `Domain\Category\CategoryRepository` (`sortTypes`, `subcategories`, `categoryDetails`, `categoryProducts`, `save`, `categoryDelete`, `saveCategoriesOrder`, `saveProductOrder`, `categoryTitle`)
|
||||
- NOWE: `admin\Controllers\ShopCategoryController` (DI) z akcjami `list/view_list`, `edit/category_edit`, `save`, `delete/category_delete`, `products/category_products`, `category_url_browser`, `save_categories_order`, `save_products_order`, `cookie_categories`
|
||||
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopCategory`
|
||||
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_category/list/`
|
||||
- UPDATE: widoki `shop-category/*` - wydzielenie skryptow do `*-custom-script.php`, ujednolicone strzalki drzewa (`button + caret + aria-expanded`)
|
||||
- UPDATE: AJAX drzewek przepiety z `/admin/ajax.php?a=*` na `/admin/shop_category/*`
|
||||
- UPDATE: zaleznosci `ShopProduct` przepiete z `admin\factory\ShopCategory` na `Domain\Category\CategoryRepository`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopCategory.php`, `autoload/admin/factory/class.ShopCategory.php`, `autoload/admin/view/class.ShopCategory.php`
|
||||
- CLEANUP: usuniety preload `class.ShopCategory.php` z `libraries/grid/config.php`
|
||||
- TEST:
|
||||
- NOWE: `tests/Unit/Domain/Category/CategoryRepositoryTest.php`
|
||||
- NOWE: `tests/Unit/admin/Controllers/ShopCategoryControllerTest.php`
|
||||
- Testy punktowe: **OK (16 tests, 72 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.274 (2026-02-15) - ShopProduct mass_edit + UI trees
|
||||
|
||||
- **ShopProduct (mass_edit)** - migracja akcji masowej edycji na Domain + DI
|
||||
- NOWE: `admin\Controllers\ShopProductController` (DI) z akcjami `mass_edit`, `mass_edit_save`, `get_products_by_category`
|
||||
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopProduct`
|
||||
- UPDATE: `Domain\Product\ProductRepository` rozszerzone o metody `allProductsForMassEdit`, `getProductsByCategory`, `applyDiscountPercent` (+ aktualizacja cen kombinacji)
|
||||
- CLEANUP: usuniete legacy akcje `mass_edit`, `mass_edit_save`, `get_products_by_category` z `admin\controls\ShopProduct`
|
||||
- **ShopProduct mass_edit UI** - przebudowa widoku i skryptu
|
||||
- UPDATE: `admin/templates/shop-product/mass-edit.php` przepiety na nowy partial JS `mass-edit-custom-script`
|
||||
- NOWE: `admin/templates/shop-product/mass-edit-custom-script.php` (nestedSortable + iCheck + stabilizacja drzewka)
|
||||
- UPDATE: `admin/templates/shop-product/subcategories-list.php` ujednolicone strzalki (button + caret)
|
||||
- FIX: zaznaczenie kategorii w drzewku nie zaznacza automatycznie produktow na liscie
|
||||
- **Pages / Articles UI** - ujednolicenie drzewek
|
||||
- UPDATE: `/admin/pages/list/` - nowe strzalki drzewa + `aria-expanded` + odswiezanie stanu branch/leaf
|
||||
- UPDATE: `/admin/articles/edit/*` (zakladka wyswietlania) - nowe strzalki i checkboxy (iCheck) dla drzewka stron
|
||||
- **ShopClients** - migracja `/admin/shop_clients` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Client\ClientRepository` (`listForAdmin`, `ordersForClient`, `totalsForClient`)
|
||||
- NOWE: `admin\Controllers\ShopClientsController` (DI) z akcjami `list`, `details` + aliasy legacy `view_list`, `clients_details`
|
||||
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopClients`
|
||||
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_clients/list/`
|
||||
- UPDATE: widoki `shop-clients/view-list` i `shop-clients/clients-details` przepiete na `components/table-list`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopClients.php`, `autoload/admin/factory/class.ShopClients.php`
|
||||
- TEST:
|
||||
- NOWE: `tests/Unit/admin/Controllers/ShopProductControllerTest.php`
|
||||
- NOWE: `tests/Unit/Domain/Client/ClientRepositoryTest.php`, `tests/Unit/admin/Controllers/ShopClientsControllerTest.php`
|
||||
- UPDATE: `tests/Unit/Domain/Product/ProductRepositoryTest.php` (nowe przypadki dla mass_edit)
|
||||
- UPDATE: `tests/bootstrap.php` (stub `S::normalize_decimal()`)
|
||||
- Testy: **OK (361 tests, 1125 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.273 (2026-02-15) - ShopProducer
|
||||
|
||||
- **ShopProducer** - migracja `/admin/shop_producer` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Producer\ProducerRepository` (`listForAdmin`, `find`, `save`, `delete`, `allProducers`, `findForFrontend`, `producerProducts`, `allActiveIds`)
|
||||
- NOWE: `admin\Controllers\ShopProducerController` (DI) z akcjami `list`, `edit`, `save`, `delete`
|
||||
- UPDATE: modul `/admin/shop_producer/*` dziala na `components/table-list` i `components/form-edit` z zakladkami jezykowymi (Opis + SEO)
|
||||
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_producer/list/`
|
||||
- UPDATE: `shop\Producer` przepiety na fasade do `Domain\Producer\ProducerRepository`
|
||||
- UPDATE: `admin\factory\ShopProduct` - 2 wywolania `admin\factory\ShopTransport` przepiete na `Domain\Transport\TransportRepository`
|
||||
- UPDATE: `admin\controls\ShopProduct` - usuniety fallback do `admin\factory\Layouts`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopProducer.php`, `admin/templates/shop-producer/list.php`, `admin/templates/shop-producer/edit.php`
|
||||
- CLEANUP: usuniete 6 pustych factory facades: `admin\factory\Languages`, `admin\factory\Newsletter`, `admin\factory\Scontainers`, `admin\factory\ShopProducer`, `admin\factory\ShopTransport`, `admin\factory\Layouts`
|
||||
- TEST: dodane `tests/Unit/Domain/Producer/ProducerRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopProducerControllerTest.php`
|
||||
- Testy: **OK (338 tests, 1063 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.272 (2026-02-15) - ShopProductSets
|
||||
|
||||
- **ShopProductSets** - migracja `/admin/shop_product_sets` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\ProductSet\ProductSetRepository` (`listForAdmin`, `find`, `save`, `delete`, `allSets`, `allProductsMap`)
|
||||
- NOWE: `admin\Controllers\ShopProductSetsController` (DI) z akcjami `list`, `edit`, `save`, `delete`
|
||||
- UPDATE: modul `/admin/shop_product_sets/*` dziala na `components/table-list` i `components/form-edit` + Selectize multi-select produktow
|
||||
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_product_sets/list/`
|
||||
- UPDATE: `shop\ProductSet` przepiety na fasade do `Domain\ProductSet\ProductSetRepository`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopProductSets.php`, `autoload/admin/factory/class.ShopProductSet.php`, `admin/templates/shop-product-sets/view-list.php`, `admin/templates/shop-product-sets/set-edit.php`
|
||||
- TEST: dodane `tests/Unit/Domain/ProductSet/ProductSetRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopProductSetsControllerTest.php`
|
||||
- Testy: **OK (324 tests, 1000 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.271 (2026-02-14) - ShopAttribute
|
||||
|
||||
- **ShopAttribute** - migracja `/admin/shop_attribute` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Attribute\AttributeRepository` (`listForAdmin`, `findAttribute`, `saveAttribute`, `deleteAttribute`, `findValues`, `saveValues`, `saveLegacyValues`, `valueDetails`)
|
||||
- NOWE: `admin\Controllers\ShopAttributeController` (DI) z akcjami `list`, `edit`, `save`, `delete`, `values`, `values_save`, `value_row_tpl`
|
||||
- UPDATE: modul `/admin/shop_attribute/*` dziala na `components/table-list` i `components/form-edit`
|
||||
- UPDATE: nowy edytor wartosci cechy (`values-edit`) z walidacja serwerowa i stabilnym `row_key` (bez indeksow do wyboru domyslnej wartosci)
|
||||
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_attribute/list/` (bez aliasow legacy)
|
||||
- UPDATE: przepiecie zaleznosci kombinacji produktu (`admin\controls\ShopProduct`, `admin\factory\ShopProduct`, `admin/templates/shop-product/product-combination.php`) na `Domain\Attribute\AttributeRepository` i `shop\ProductAttribute`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopAttribute.php`, `autoload/admin/factory/class.ShopAttribute.php`, `autoload/admin/view/class.ShopAttribute.php`, `admin/templates/shop-attribute/_partials/value.php`
|
||||
- TEST: dodane `tests/Unit/Domain/Attribute/AttributeRepositoryTest.php` i `tests/Unit/admin/Controllers/ShopAttributeControllerTest.php`
|
||||
- Testy: **OK (312 tests, 948 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.270 (2026-02-14) - Apilo payment/status sync hardening
|
||||
|
||||
- **Shop/Order + Apilo** - utwardzenie synchronizacji platnosci i statusow zamowien
|
||||
- FIX: `shop\Order::set_as_paid()` wysyla do Apilo mapowany typ platnosci (`payment_method_id` -> `apilo_payment_type_id`) zamiast stalego `type = 1`
|
||||
- NOWE: retry queue dla chwilowej niedostepnosci Apilo (`temp/apilo-sync-queue.json`) dla sync platnosci i statusu
|
||||
- NOWE: `shop\Order::process_apilo_sync_queue()` przetwarza zalegle syncy
|
||||
- UPDATE: `cron.php` uruchamia przetwarzanie kolejki sync Apilo przy aktywnej integracji
|
||||
- UPDATE: rozszerzone logowanie debug (`logs/apilo.txt`) o HTTP code i bledy cURL dla sync platnosci/statusu
|
||||
- Testy: **OK (300 tests, 895 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.269 (2026-02-14) - ShopTransport
|
||||
|
||||
- **ShopTransport** - migracja `/admin/shop_transport` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Transport\TransportRepository` (`listForAdmin`, `find`, `save`, `allActive`, `allForAdmin`, `findActiveById`, `getTransportCost`, `lowestTransportPrice`, `getApiloCarrierAccountId`)
|
||||
- NOWE: `admin\Controllers\ShopTransportController` (DI) z akcjami `list`, `edit`, `save`
|
||||
- NOWE: widoki `shop-transport/transports-list.php` i `shop-transport/transport-edit.php` + `transport-edit-custom-script.php`
|
||||
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_transport/list/`
|
||||
- UPDATE: `admin\factory\ShopTransport`, `front\factory\ShopTransport` przepiete na nowe repozytorium
|
||||
- FIX: `save()` return type `?int` zamiast `int|bool` (spojnosc z PaymentMethod)
|
||||
- FIX: `toSwitchValue()` helper zamiast `=== 'on'` (obsluga '1', 'on', 'true', 'yes')
|
||||
- FIX: `\S::delete_dir()` przeniesione z repozytorium do kontrolera (DDD)
|
||||
- FIX: Medoo `select()` syntax - ORDER w WHERE zamiast 4-arg form
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopTransport.php`, `autoload/admin/view/class.ShopTransport.php`, `admin/templates/shop-transport/view-list.php`
|
||||
- FIX: `transports-list.php` - zmienna `'viewModel'` zmieniona na `'list'` (zgodnie z `table-list.php` komponentem)
|
||||
- Testy: **OK (300 tests, 895 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.268 (2026-02-14) - ShopPaymentMethod + Apilo token keepalive
|
||||
|
||||
- **ShopPaymentMethod** - migracja `/admin/shop_payment_method` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\PaymentMethod\PaymentMethodRepository` (`listForAdmin`, `find`, `save`, `allActive`, `allForAdmin`, `findActiveById`, `isActive`, `getApiloPaymentTypeId`, `forTransport`)
|
||||
- NOWE: `admin\Controllers\ShopPaymentMethodController` (DI) z akcjami `list`, `edit`, `save`
|
||||
- NOWE: widoki `shop-payment-method/payment-methods-list.php` i `shop-payment-method/payment-method-edit.php`
|
||||
- UPDATE: routing i menu admin na kanoniczny URL `/admin/shop_payment_method/list/`
|
||||
- UPDATE: `admin\controls\ShopTransport`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod` przepiete na nowe repozytorium
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopPaymentMethod.php`, `autoload/admin/factory/class.ShopPaymentMethod.php`, `autoload/admin/view/class.ShopPaymentMethod.php`, `admin/templates/shop-payment-method/view-list.php`
|
||||
- **Integrations/Apilo** - stabilizacja tokenu i lepszy feedback
|
||||
- NOWE: automatyczne odswiezanie tokenu Apilo przed wygasnieciem (`apiloKeepalive`, refresh lead time)
|
||||
- UPDATE: cron uruchamia keepalive i odswieza konfiguracje Apilo
|
||||
- UPDATE: bardziej szczegolowe komunikaty bledow dla przyciskow integracji Apilo (co zrobic dalej)
|
||||
- Testy: **OK (280 tests, 828 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.267 (2026-02-14) - ShopStatuses
|
||||
|
||||
- **ShopStatuses** - migracja `/admin/shop_statuses` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\ShopStatus\ShopStatusRepository` (`listForAdmin`, `find`, `save`, `getApiloStatusId`, `getByIntegrationStatusId`, `allStatuses`)
|
||||
- NOWE: `admin\Controllers\ShopStatusesController` (DI) z akcjami `list`, `edit`, `save` (bez aliasow legacy)
|
||||
- NOWE: typ pola `FormFieldType::COLOR` + `FormField::color()` + `FormFieldRenderer::renderColor()` (color picker HTML5 zsynchronizowany z polem tekstowym)
|
||||
- UPDATE: modul `/admin/shop_statuses/*` dziala na `components/table-list` i `components/form-edit`
|
||||
- UPDATE: `front\factory\ShopStatuses` jako fasada delegujaca do `Domain\ShopStatus\ShopStatusRepository`
|
||||
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_statuses/list/`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopStatuses.php`, `autoload/admin/factory/class.ShopStatuses.php`
|
||||
- UWAGA: statusy maja ID od 0 - kluczowe dla walidacji (find/save uzywaja `$id < 0`)
|
||||
- Testy: **OK (254 tests, 736 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.266 (2026-02-13) - ShopCoupon
|
||||
|
||||
- **ShopCoupon** - migracja `/admin/shop_coupon` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Coupon\CouponRepository` (`listForAdmin`, `find`, `save`, `delete`, `categoriesTree`)
|
||||
- NOWE: `admin\Controllers\ShopCouponController` (DI) z akcjami `list`, `edit`, `save`, `delete`
|
||||
- UPDATE: kompatybilnosc aliasow legacy (`view_list`, `coupon_edit`, `coupon_save`, `coupon_delete`) obslugiwana przez nowy kontroler
|
||||
- UPDATE: modul `/admin/shop_coupon/*` dziala na `components/table-list` i `components/form-edit`
|
||||
- NOWE: widoki/partiale `shop-coupon/coupons-list`, `shop-coupon/coupon-edit-new`, `shop-coupon/coupon-categories-selector`, `shop-coupon/coupon-categories-tree`, `shop-coupon/coupon-edit-custom-script`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopCoupon.php`, `autoload/admin/factory/class.ShopCoupon.php`, `admin/templates/shop-coupon/view-list.php`, `admin/templates/shop-coupon/coupon-edit.php`
|
||||
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_coupon/list/`
|
||||
- FIX: ujednolicone UI drzewek i checkboxow miedzy kuponami i layoutami
|
||||
- Testy: **OK (235 tests, 682 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.265 (2026-02-13) - ShopPromotion poprawki
|
||||
|
||||
- **ShopPromotion** - stabilizacja po migracji
|
||||
- UPDATE: dodane `date_from` w `Domain\Promotion\PromotionRepository` (save/find/list/sort)
|
||||
- UPDATE: `admin\Controllers\ShopPromotionController` rozszerzony o pole `Data od` na formularzu i kolumne `Data od` na liscie
|
||||
- UPDATE: `shop\Promotion::get_active_promotions()` filtruje aktywnosc po `date_from` i `date_to`
|
||||
- FIX: zapis edycji promocji nie tworzy nowego rekordu (hidden `id` + fallback `id` z URL)
|
||||
- TEST: rozszerzono `PromotionRepositoryTest` o asercje `date_from`
|
||||
- Testy: **OK (222 tests, 614 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.264 (2026-02-13) - ShopPromotion
|
||||
|
||||
- **ShopPromotion** - migracja `/admin/shop_promotion` na Domain + DI + nowe widoki
|
||||
- NOWE: `Domain\Promotion\PromotionRepository` (`listForAdmin`, `find`, `save`, `delete`, `categoriesTree`, invalidacja cache aktywnych promocji)
|
||||
- NOWE: `admin\Controllers\ShopPromotionController` (DI) z akcjami `list`, `edit`, `save`, `delete`
|
||||
- UPDATE: routing DI (`admin\Site`) rozszerzony o modul `ShopPromotion`
|
||||
- UPDATE: modul `/admin/shop_promotion/*` dziala na `components/table-list` i `components/form-edit`
|
||||
- NOWE: widoki/partiale `shop-promotion/promotions-list`, `shop-promotion/promotion-edit`, `shop-promotion/promotion-categories-selector`, `shop-promotion/promotion-categories-tree`, `shop-promotion/promotion-edit-custom-script`
|
||||
- CLEANUP: usuniete legacy `autoload/admin/controls/class.ShopPromotion.php`, `autoload/admin/factory/class.ShopPromotion.php`, `admin/templates/shop-promotion/view-list.php`
|
||||
- UPDATE: menu admin przepiete na kanoniczny URL `/admin/shop_promotion/list/`
|
||||
- Testy: **OK (222 tests, 609 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.263 (2026-02-13) - Integrations + cleanup Sellasist/Baselinker
|
||||
|
||||
- NOWE: `Domain\Integrations\IntegrationsRepository` (settings Apilo/ShopPRO, OAuth, product linking, API fetch)
|
||||
- NOWE: `admin\Controllers\IntegrationsController` (DI) dla akcji Apilo i ShopPRO
|
||||
- UPDATE: `admin\factory\Integrations` jako fasada delegujaca do repozytorium
|
||||
- **CLEANUP: usunieto integracje Sellasist i Baselinker z calego projektu:**
|
||||
- Usuniete klasy: `admin\controls\Integrations`, `admin\controls\Baselinker`, `admin\factory\Baselinker`, `front\factory\Shop`, `shop\ShopStatus`
|
||||
- Usuniete szablony: `integrations/sellasist-settings.php`, `integrations/baselinker-settings.php`, `admin/templates/baselinker/`
|
||||
- Wyczyszczone referencje w: `cron.php`, `cron/cron-xml.php`, `shop\Order`, kontrolery/factory/front Shop*
|
||||
- Testy: **OK (212 tests, 577 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.262 (2026-02-13) - Pages
|
||||
|
||||
- NOWE: `Domain\Pages\PagesRepository` (CRUD menu/stron, drzewo stron, sortowanie, SEO)
|
||||
- NOWE: `admin\Controllers\PagesController` (DI) dla akcji menu/page/AJAX
|
||||
- UPDATE: widoki `admin/templates/pages/*` przepiete na dane z kontrolera/repozytorium
|
||||
- UPDATE: endpointy AJAX przepiete z `admin/ajax.php?a=*` na `/admin/pages/*`
|
||||
- CLEANUP: usuniete legacy `controls/Pages`, `view/Pages`, `factory/Pages`, `ajax/pages.php`
|
||||
- Testy: **OK (186 tests, 478 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.261 (2026-02-13) - Articles (dalsza refaktoryzacja)
|
||||
|
||||
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o metody UI/admin i `saveFilesOrder()`
|
||||
- UPDATE: `admin\Controllers\ArticlesController` obsluguje AJAX: `article_image_alt_change`, `article_file_name_change`, `article_image_delete`, `article_file_delete`, `filesOrderSave`
|
||||
- UPDATE: lista artykulow nie korzysta juz z `admin\factory\Articles::article_pages()`
|
||||
- UPDATE: widok edycji przepiety z `/admin/ajax.php` na `/admin/articles/*`
|
||||
- UPDATE: drag&drop sortowania listy zalacznikow
|
||||
- CLEANUP: usuniete `autoload/admin/view/class.Articles.php` i `admin/ajax/articles.php`
|
||||
- Testy: **OK (178 tests, 443 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.260 (2026-02-12) - ArticlesArchive
|
||||
|
||||
- NOWE: `admin\Controllers\ArticlesArchiveController` (DI)
|
||||
- UPDATE: `Domain\Article\ArticleRepository` rozszerzone o `listArchivedForAdmin()`, `restore()`, `deletePermanently()`
|
||||
- UPDATE: `/admin/articles_archive/view_list/` migrowane na `components/table-list`
|
||||
- CLEANUP: usuniete legacy `controls/ArticlesArchive`, `factory/ArticlesArchive`, `view/ArticlesArchive`
|
||||
- Testy: **OK (165 tests, 424 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.259 (2026-02-12) - Scontainers
|
||||
|
||||
- NOWE: `Domain\Scontainers\ScontainersRepository` (listForAdmin, find, save, delete, detailsForLanguage)
|
||||
- NOWE: `admin\Controllers\ScontainersController` (DI)
|
||||
- UPDATE: `/admin/scontainers/*` migrowane na `components/table-list` i `components/form-edit`
|
||||
- UPDATE: `admin\factory\Scontainers` i `front\factory\Scontainers` jako fasady
|
||||
- CLEANUP: usuniete `controls/Scontainers`, `view/Scontainers`
|
||||
- Testy: **OK (158 tests, 397 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.258 (2026-02-12) - Newsletter (stabilizacja)
|
||||
|
||||
- UPDATE: tymczasowo wylaczono flow `prepare/send/preview` (wymaga przebudowy)
|
||||
- UPDATE: tymczasowo wylaczono modul `Szablony uzytkownika`
|
||||
- UPDATE: aktywna obsluga tylko szablonow administracyjnych (`is_admin = 1`)
|
||||
- CLEANUP: usuniete nieuzywane widoki `prepare.php`, `preview.php`, `email-templates-user.php`
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.257 (2026-02-12) - Newsletter
|
||||
|
||||
- NOWE: `Domain\Newsletter\NewsletterRepository` (subskrybenci, szablony, ustawienia, kolejka wysylki)
|
||||
- NOWE: `Domain\Newsletter\NewsletterPreviewRenderer` (render podgladu)
|
||||
- NOWE: `admin\Controllers\NewsletterController` (DI)
|
||||
- UPDATE: `/admin/newsletter/*` migrowane na `components/table-list` i `components/form-edit`
|
||||
- UPDATE: `admin\factory\Newsletter` jako fasada; `front\factory\Newsletter` bez `admin\view\Newsletter`
|
||||
- CLEANUP: usuniete `controls/Newsletter`, `view/Newsletter`
|
||||
- Testy: **OK (150 tests, 372 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.256 (2026-02-12) - Layouts
|
||||
|
||||
- NOWE: `Domain\Layouts\LayoutsRepository` (find, save, delete, listForAdmin, menusWithPages, categoriesTree)
|
||||
- NOWE: `admin\Controllers\LayoutsController` (DI)
|
||||
- UPDATE: lista `/admin/layouts/view_list/` migrowana na `components/table-list`
|
||||
- UPDATE: widok `layouts/layout-edit` korzysta z danych z repozytorium
|
||||
- NOWE: partial `admin/templates/layouts/subcategories-list.php`
|
||||
- UPDATE: `Domain\Languages\LanguagesRepository::defaultLanguageId()` jako wspolna metoda
|
||||
- UPDATE: `ArticlesController` korzysta z `LayoutsRepository` (DI)
|
||||
- CLEANUP: usuniete `controls/Layouts`, `view/Layouts`; `factory/Layouts` jako fasada
|
||||
- Testy: **OK (141 tests, 336 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.255 (2026-02-12) - Languages DI cleanup
|
||||
|
||||
- UPDATE: SettingsController, BannerController, DictionariesController, ArticlesController pobieraja liste jezykow przez `Domain/Languages/LanguagesRepository` (DI)
|
||||
- UPDATE: router DI przekazuje `LanguagesRepository` do kontrolerow
|
||||
- UPDATE: legacy `admin/controls`, `admin/factory/Shop*` przepiete na `LanguagesRepository`
|
||||
- FIX: `admin/factory/class.Languages.php` poprawione na `<?php` (short_open_tag=Off)
|
||||
- Testy: **OK (130 tests, 303 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.254 (2026-02-12) - Languages
|
||||
|
||||
- NOWE: `Domain\Languages\LanguagesRepository` (languages + translations CRUD/list)
|
||||
- NOWE: `admin\Controllers\LanguagesController` (DI)
|
||||
- UPDATE: widoki `languages/*` migrowane na `components/table-list` i `components/form-edit`
|
||||
- CLEANUP: usuniete `controls/Languages`, `view/Languages`; `factory/Languages` jako fasada
|
||||
- Testy: **OK (130 tests, 301 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.253 (2026-02-12) - Users
|
||||
|
||||
- NOWE: `Domain\User\UserRepository` (CRUD, logon, 2FA, checkLogin)
|
||||
- NOWE: `admin\Controllers\UsersController` (DI: view_list, user_edit, user_save, user_delete, login_form, twofa)
|
||||
- UPDATE: `admin\factory\Users` jako fasada; `admin/ajax/users.php` oparty o `UserRepository`
|
||||
- UPDATE: widoki users migrowane na `components/table-list` i `components/form-edit`
|
||||
- UPDATE: walidacja warunkowa: `twofa_email` wymagany gdy `twofa_enabled = 1`
|
||||
- CLEANUP: usuniete `controls/Users`, `factory/Users`, `view/Users`
|
||||
- Testy: **OK (119 tests, 256 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.252 (2026-02-10) - ProductArchive + Filemanager
|
||||
|
||||
- UPDATE: `ProductArchiveController` i szablony przepiete na `components/table-list`
|
||||
- UPDATE: CSS/JS dla list wydzielone do osobnych widoków `*-custom-script.php`
|
||||
- NOWE: `admin\Controllers\FilemanagerController` - przepieto filemanager na nowy routing
|
||||
- FIX: naprawiono błąd `Invalid Key` w filemanagerze
|
||||
- CLEANUP: usunieto legacy `controls/Archive`, `controls/Filemanager`, `view/FileManager`, stare szablony
|
||||
- RENAME: `admin/templates/product_archive/` → `admin/templates/product-archive/`
|
||||
- Testy: **OK (82 tests, 181 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.251 (2026-02-09) - Dictionaries
|
||||
|
||||
- NOWE: `Domain\Dictionaries\DictionariesRepository` (listForAdmin, find, save, delete, allUnits)
|
||||
- NOWE: `admin\Controllers\DictionariesController` (lista + formularz na nowych komponentach)
|
||||
- UPDATE: migracja słowników na `components/table-list` i `components/form-edit`
|
||||
- FIX: obsługa `lang_id` jako string (`pl`, `en`) w zapisie tłumaczeń
|
||||
- CLEANUP: usunięto legacy klasy Dictionaries (`admin\controls`, `admin\factory`, `front\factory`)
|
||||
- Testy: **OK (82 tests, 181 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.250 (2026-02-09) - Settings (refaktoryzacja)
|
||||
|
||||
- UPDATE: `Domain\Settings\SettingsRepository` ma bezpośredni dostęp do DB (bez delegacji do `admin\factory\Settings`)
|
||||
- UPDATE: przepięto użycia `admin\factory\Settings` na `Domain\Settings\SettingsRepository`
|
||||
- CLEANUP: usunięto legacy klasy Settings (`factory`, `controls`, `view`)
|
||||
- Testy: **OK (82 tests, 181 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.246 (2026-02-07) - Articles legacy cleanup
|
||||
|
||||
- CLEANUP: usunięto `autoload/admin/controls/class.Articles.php`
|
||||
- UPDATE: `admin\Controllers\ArticlesController::galleryOrderSave()` uzywa `ArticleRepository::saveGalleryOrder()`
|
||||
- FIX: sortowanie list admin po reloadzie - `RewriteRule` dla `/admin/...` ma `QSA`
|
||||
- FIX: generator `\S::htacces()` komentuje dyrektywy `AddHandler|SetHandler|ForceType`
|
||||
- Testy: **OK (65 tests, 131 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.245 (2026-02-06) - Articles::archive
|
||||
|
||||
- UPDATE: `Domain\Article\ArticleRepository` - dodano `archive()` (status = -1)
|
||||
- UPDATE: `admin\Controllers\ArticlesController` - nowa akcja `delete()` z DI
|
||||
- UPDATE: `admin\factory\Articles::articles_set_archive()` deleguje do repozytorium
|
||||
- Testy: **OK (59 tests, 123 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.244 (2026-02-06) - Articles::save
|
||||
|
||||
- UPDATE: `Domain\Article\ArticleRepository` - dodano `save()` z prywatnych helperow
|
||||
- UPDATE: `admin\Controllers\ArticlesController` - nowa akcja `save()` z DI
|
||||
- UPDATE: `tests/bootstrap.php` - dodano stub `S::seo()`
|
||||
- Testy: **OK (57 tests, 119 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.243 (2026-02-06) - Articles cleanup
|
||||
|
||||
- UPDATE: `Domain\Article\ArticleRepository` - dodano `deleteNonassignedImages()` i `deleteNonassignedFiles()`
|
||||
- UPDATE: `admin\Controllers\ArticlesController::edit()` uses repository cleanup methods
|
||||
- Testy: **OK (50 tests, 95 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.242 (2026-02-06) - Articles::edit
|
||||
|
||||
- NOWE: `Domain\Article\ArticleRepository` (find: artykuł + relacje)
|
||||
- UPDATE: `admin\Controllers\ArticlesController` - konstruktor DI + `edit()` używa repozytorium
|
||||
- UPDATE: `admin\factory\Articles::article_details()` deleguje do repozytorium
|
||||
- Testy: **OK (48 tests, 91 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.241 (2026-02-06) - ProductArchive
|
||||
|
||||
- NOWE: `admin\Controllers\ProductArchiveController` (DI)
|
||||
- NOWE: `ProductRepository::archive()`, `unarchive()`
|
||||
- RENAME: `admin/templates/archive/` → `admin/templates/product_archive/`
|
||||
- FIX: SQL w `ajax_products_list_archive()` (puste wyszukiwanie + brak `archive = 1`)
|
||||
- Testy: **OK (50 tests, 95 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.240 (2026-02-05) - Settings + Cache
|
||||
|
||||
- NOWE: `Domain\Settings\SettingsRepository` (fasada → factory)
|
||||
- NOWE: `Domain\Cache\CacheRepository` (dirs + Redis)
|
||||
- NOWE: `admin\Controllers\SettingsController` (DI: clearCache, save, view)
|
||||
- FIX: Brakujący `id="content"` w main-layout.php
|
||||
- FIX: `persist_edit = true` w settings.php
|
||||
- Testy: **OK (29 tests, 60 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.239 (2026-02-05) - Banner + Product
|
||||
|
||||
- NOWE: `Domain\Banner\BannerRepository` (find, delete, save)
|
||||
- NOWE: `admin\Controllers\BannerController` - pierwszy kontroler z DI
|
||||
- NOWE: Router z mapą `$newControllers` + fallback na stare kontrolery
|
||||
- NOWE: Autoloader PSR-4 fallback w 9 entry pointach
|
||||
- Zmigrowano: `get_product_price()` → `ProductRepository::getPrice()`
|
||||
- Zmigrowano: `get_product_name()` → `ProductRepository::getName()`
|
||||
- Testy: **OK (15 tests, 31 assertions)**
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.238 (2025-02-05) - Product Repository
|
||||
|
||||
- NOWE: `Domain\Product\ProductRepository` - pierwsza klasa w nowej architekturze
|
||||
- NOWE: Dependency Injection zamiast `global $mdb`
|
||||
- NOWE: Testy jednostkowe (5 testów, 100% pokrycie)
|
||||
- Zmigrowano: `get_product_quantity()` → `ProductRepository::getQuantity()`
|
||||
- Kompatybilność: Stara klasa `shop\Product` działa jako fasada
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.237 (2025-02-05) - System cache produktów
|
||||
|
||||
- Automatyczne czyszczenie cache produktu po aktualizacji przez CRON
|
||||
- AJAX dla przycisku "Wyczyść cache" w panelu admin
|
||||
- Metody `delete()` i `deletePattern()` w CacheHandler
|
||||
- Metoda `clear_product_cache()` w klasie S
|
||||
|
||||
---
|
||||
*Dokument aktualizowany: 2026-02-14*
|
||||
630
temp/update_build/tmp_0.275/docs/DATABASE_STRUCTURE.md
Normal file
630
temp/update_build/tmp_0.275/docs/DATABASE_STRUCTURE.md
Normal file
@@ -0,0 +1,630 @@
|
||||
# Struktura bazy danych shopPRO
|
||||
|
||||
Plik aktualizowany na bieżąco przy zmianach w kodzie.
|
||||
ORM: Medoo (`$mdb`), prefix tabel: `pp_`
|
||||
|
||||
## pp_shop_products
|
||||
Główna tabela produktów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| parent_id | FK do produktu nadrzędnego (kombinacje) - NULL dla produktów głównych |
|
||||
| price_brutto | Cena brutto |
|
||||
| price_brutto_promo | Cena promocyjna brutto |
|
||||
| quantity | Stan magazynowy |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| archive | Archiwum: 1 = zarchiwizowany, 0 = aktywny |
|
||||
| promoted | Czy promowany |
|
||||
| vat | Stawka VAT |
|
||||
| ean | Kod EAN |
|
||||
| sku | Kod SKU |
|
||||
| apilo_product_id | ID produktu w Apilo |
|
||||
| apilo_product_name | Nazwa produktu w Apilo |
|
||||
|
||||
**Używane w:** `Domain\Product\ProductRepository`, `admin\factory\ShopProduct`, `admin\Controllers\ShopProductController`
|
||||
|
||||
## pp_shop_products_langs
|
||||
Tłumaczenia produktów (per język).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| product_id | FK do pp_shop_products |
|
||||
| lang_id | ID języka (np. 'pl') |
|
||||
| name | Nazwa produktu |
|
||||
|
||||
**Używane w:** `Domain\Product\ProductRepository::getName()`
|
||||
|
||||
## pp_shop_products_images
|
||||
Zdjęcia produktów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| product_id | FK do pp_shop_products |
|
||||
| src | Ścieżka do pliku |
|
||||
| alt | Tekst alternatywny |
|
||||
|
||||
## pp_shop_products_categories
|
||||
Przypisanie produktów do kategorii.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| product_id | FK do pp_shop_products |
|
||||
|
||||
**Używane w:** `admin\factory\ShopProduct::product_delete()`, `Domain\Product\ProductRepository::getProductsByCategory()`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.274):** akcje `/admin/shop_product/mass_edit/*` korzystają z `Domain\Product\ProductRepository` przez `admin\Controllers\ShopProductController`.
|
||||
|
||||
## pp_shop_categories
|
||||
Kategorie sklepu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| parent_id | FK do kategorii nadrzednej (NULL dla root) |
|
||||
| status | 1 = aktywna, 0 = nieaktywna |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
| sort_type | Typ sortowania produktow w kategorii |
|
||||
| view_subcategories | Czy wyswietlac podkategorie |
|
||||
|
||||
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
|
||||
|
||||
## pp_shop_categories_langs
|
||||
Tlumaczenia kategorii (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| category_id | FK do pp_shop_categories |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| title | Nazwa kategorii |
|
||||
| text | Opis kategorii |
|
||||
| text_hidden | Rozwiniecie opisu kategorii |
|
||||
| seo_link | Link SEO kategorii |
|
||||
| meta_title | Meta title |
|
||||
| meta_description | Meta description |
|
||||
| meta_keywords | Meta keywords |
|
||||
| noindex | Flaga noindex |
|
||||
| category_title | Naglowek H1 kategorii |
|
||||
| additional_text | Dodatkowy tekst nad lista produktow |
|
||||
|
||||
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.275):** modul `/admin/shop_category/*` korzysta z `Domain\Category\CategoryRepository` przez `admin\Controllers\ShopCategoryController`; usunieto legacy `admin\controls/factory/view\ShopCategory`.
|
||||
|
||||
## pp_shop_orders
|
||||
Zamówienia sklepu (źródło danych dla list i szczegółów klientów w panelu admin).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| client_id | FK do `pp_shop_clients` (NULL dla gościa) |
|
||||
| client_name | Imię klienta z zamówienia |
|
||||
| client_surname | Nazwisko klienta z zamówienia |
|
||||
| client_email | E-mail klienta z zamówienia |
|
||||
| client_phone | Telefon klienta |
|
||||
| client_city | Miasto klienta |
|
||||
| summary | Wartość zamówienia |
|
||||
| date_order | Data złożenia zamówienia |
|
||||
| payment_method | Nazwa metody płatności |
|
||||
| transport | Nazwa transportu |
|
||||
| message | Wiadomość klienta |
|
||||
|
||||
**Używane w:** `Domain\Client\ClientRepository::listForAdmin()`, `Domain\Client\ClientRepository::ordersForClient()`, `Domain\Client\ClientRepository::totalsForClient()`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.274):** moduł `/admin/shop_clients/*` korzysta z `Domain\Client\ClientRepository` przez `admin\Controllers\ShopClientsController`.
|
||||
|
||||
## pp_banners
|
||||
Banery.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa banera |
|
||||
| status | 0/1 |
|
||||
| date_start | Data rozpoczęcia |
|
||||
| date_end | Data zakończenia |
|
||||
| home_page | Czy na stronie głównej 0/1 |
|
||||
|
||||
**Używane w:** `Domain\Banner\BannerRepository`
|
||||
|
||||
## pp_banners_langs
|
||||
Tłumaczenia banerów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| id_banner | FK do pp_banners |
|
||||
| id_lang | ID języka |
|
||||
| src | Ścieżka do grafiki |
|
||||
| url | URL docelowy |
|
||||
| html | Kod HTML |
|
||||
| text | Tekst |
|
||||
|
||||
**Używane w:** `Domain\Banner\BannerRepository`
|
||||
|
||||
## pp_articles
|
||||
Artykuły.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny |
|
||||
|
||||
**Używane w:** `admin\Controllers\ArticlesArchiveController`, `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::listArchivedForAdmin()`
|
||||
|
||||
## pp_articles_pages
|
||||
Strony artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| page_id | FK do strony (pp_pages) |
|
||||
| o | Kolejność |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedImages()`
|
||||
|
||||
## pp_articles_langs
|
||||
Tłumaczenia artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| lang_id | ID języka (np. 'pl') |
|
||||
| title | Tytuł artykułu |
|
||||
| seo_link | Link SEO artykułu |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedFiles()`
|
||||
|
||||
## pp_articles_images
|
||||
Zdjęcia artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| src | Ścieżka do pliku |
|
||||
| o | Kolejność |
|
||||
| id | PK (używane też do sortowania DESC) |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`
|
||||
|
||||
## pp_articles_files
|
||||
Pliki artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| article_id | FK do pp_articles |
|
||||
| src | Ścieżka do pliku |
|
||||
| name | Nazwa wyświetlana załącznika (opcjonalna) |
|
||||
| to_delete | Flaga miękkiego usuwania (0/1) |
|
||||
| o | Kolejność załączników (używana przez sortowanie drag&drop w adminie) |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::saveFilesOrder()`
|
||||
|
||||
## pp_units
|
||||
Jednostki/slowniki (np. jednostki produktu).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
|
||||
**Używane w:** `Domain\Dictionaries\DictionariesRepository`, `admin\controls\ShopProduct`
|
||||
|
||||
## pp_units_langs
|
||||
Tlumaczenia jednostek (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| unit_id | FK do pp_units |
|
||||
| lang_id | ID jezyka (np. 'pl') |
|
||||
| text | Nazwa jednostki |
|
||||
|
||||
**Używane w:** `Domain\Dictionaries\DictionariesRepository`
|
||||
|
||||
## pp_users
|
||||
Uzytkownicy panelu administratora.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| login | Login / e-mail uzytkownika |
|
||||
| password | Hash hasla (legacy: md5) |
|
||||
| status | Status konta: 1 = aktywny, 0 = zablokowany |
|
||||
| admin | Flaga dostepu do panelu admin |
|
||||
| error_logged_count | Licznik nieudanych logowan |
|
||||
| last_logged | Data ostatniego poprawnego logowania |
|
||||
| last_error_logged | Data ostatniej nieudanej proby logowania |
|
||||
| twofa_enabled | Czy wlaczone 2FA (0/1) |
|
||||
| twofa_email | E-mail do wysylki kodu 2FA |
|
||||
| twofa_code_hash | Hash aktualnego kodu 2FA |
|
||||
| twofa_expires_at | Data waznosci kodu 2FA |
|
||||
| twofa_sent_at | Data ostatniej wysylki kodu 2FA |
|
||||
| twofa_failed_attempts | Liczba nieudanych prob 2FA |
|
||||
|
||||
**Uzywane w:** `Domain\User\UserRepository`, `admin\Controllers\UsersController`, `admin\factory\Users`
|
||||
|
||||
**Aktualizacja 2026-02-12:** uzycia `pp_users` sa prowadzone przez `Domain\\User\\UserRepository` (legacy `admin\\factory\\Users` usunieto).
|
||||
|
||||
## pp_langs
|
||||
Jezyki panelu i frontendu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK (2-literowe ID jezyka, np. pl, en) |
|
||||
| name | Nazwa jezyka |
|
||||
| status | 1 = aktywny, 0 = nieaktywny |
|
||||
| start | 1 = domyslny jezyk |
|
||||
| o | Kolejnosc |
|
||||
|
||||
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
|
||||
|
||||
## pp_langs_translations
|
||||
Slownik tlumaczen panelu/frontendu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| text | Klucz/tekst bazowy |
|
||||
| <lang_id> | Kolumny dynamiczne per jezyk (np. pl, en) |
|
||||
|
||||
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
|
||||
|
||||
**Aktualizacja 2026-02-12:** modul jezykow i tlumaczen (`pp_langs`, `pp_langs_translations`) obslugiwany przez `Domain\\Languages\\LanguagesRepository`.
|
||||
|
||||
## pp_layouts
|
||||
Szablony layoutow (HTML/CSS/JS + flagi domyslne).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa szablonu |
|
||||
| html | Kod HTML |
|
||||
| css | Kod CSS |
|
||||
| js | Kod JS |
|
||||
| m_html | Kod HTML mobilny |
|
||||
| m_css | Kod CSS mobilny |
|
||||
| m_js | Kod JS mobilny |
|
||||
| status | Domyslny layout stron (0/1) |
|
||||
| categories_default | Domyslny layout kategorii (0/1) |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `admin\\Controllers\\LayoutsController`, `front\\factory\\Layouts`
|
||||
|
||||
## pp_layouts_pages
|
||||
Przypisanie layoutow do stron CMS.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| layout_id | FK do pp_layouts |
|
||||
| page_id | FK do pp_pages |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
|
||||
|
||||
## pp_layouts_categories
|
||||
Przypisanie layoutow do kategorii sklepu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| layout_id | FK do pp_layouts |
|
||||
| category_id | FK do pp_shop_categories |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.256):** modul `/admin/layouts` korzysta z `Domain\\Layouts\\LayoutsRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
## pp_newsletter
|
||||
Adresy e-mail zapisane do newslettera.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| email | Adres e-mail subskrybenta |
|
||||
| hash | Hash potwierdzenia/wypisu |
|
||||
| status | 1 = potwierdzony, 0 = oczekujacy |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\factory\\Newsletter`
|
||||
|
||||
## pp_newsletter_send
|
||||
Kolejka wysylki newslettera.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| email | Adres docelowy |
|
||||
| dates | Zakres dat artykulow (tekst) |
|
||||
| id_template | FK do `pp_newsletter_templates` (NULL gdy brak szablonu) |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\factory\\Newsletter::newsletter_send()`
|
||||
|
||||
## pp_newsletter_templates
|
||||
Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa/klucz szablonu |
|
||||
| text | Tresc HTML szablonu |
|
||||
| is_admin | 1 = szablon administracyjny/systemowy, 0 = szablon uzytkownika |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`, `front\\factory\\Newsletter`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
## pp_scontainers
|
||||
Kontenery statyczne (modul /admin/scontainers).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | 1 = aktywny, 0 = nieaktywny |
|
||||
| show_title | 1 = pokaz tytul, 0 = ukryj tytul |
|
||||
|
||||
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `admin\Controllers\ScontainersController`, `front\factory\Scontainers`
|
||||
|
||||
## pp_scontainers_langs
|
||||
Tlumaczenia kontenerow statycznych (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| container_id | FK do pp_scontainers |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| title | Tytul kontenera |
|
||||
| text | Tresc HTML kontenera |
|
||||
|
||||
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `front\factory\Scontainers`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.259):** modul `/admin/scontainers` korzysta z `Domain\Scontainers\ScontainersRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.260):** modul `/admin/articles_archive` korzysta z `Domain\Article\ArticleRepository` (`listArchivedForAdmin`, `restore`, `deletePermanently`) przez `admin\Controllers\ArticlesArchiveController`.
|
||||
|
||||
## pp_shop_attributes
|
||||
Cechy produktu (modul `/admin/shop_attribute`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| type | Typ cechy: 0 = tekst, 1 = kolor, 2 = wzor |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
## pp_shop_attributes_langs
|
||||
Tlumaczenia cech produktu (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| attribute_id | FK do pp_shop_attributes |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| name | Nazwa cechy |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
|
||||
|
||||
## pp_shop_attributes_values
|
||||
Wartosci cech produktu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| attribute_id | FK do pp_shop_attributes |
|
||||
| is_default | Czy wartosc domyslna dla cechy (0/1) |
|
||||
| impact_on_the_price | Wplyw na cene wariantu (NULL = brak) |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\factory\ShopProduct`
|
||||
|
||||
## pp_shop_attributes_values_langs
|
||||
Tlumaczenia wartosci cech (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| value_id | FK do pp_shop_attributes_values |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| name | Nazwa wyswietlana |
|
||||
| value | Wewnetrzna wartosc techniczna (opcjonalna) |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
|
||||
|
||||
## pp_shop_products_attributes
|
||||
Powiazanie kombinacji produktow z wartosciami cech.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| product_id | FK do pp_shop_products (kombinacja) |
|
||||
| value_id | FK do pp_shop_attributes_values |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository::refreshCombinationPricesForValue()`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.271):** modul `/admin/shop_attribute` korzysta z `Domain\Attribute\AttributeRepository` przez `admin\Controllers\ShopAttributeController`. Usunieto legacy klasy `admin\controls\ShopAttribute`, `admin\factory\ShopAttribute`, `admin\view\ShopAttribute`.
|
||||
|
||||
## pp_shop_coupon
|
||||
Kody rabatowe sklepu (modul `/admin/shop_coupon`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Kod kuponu (UNIQUE) |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| send | Czy kupon zostal wyslany (0/1) |
|
||||
| used | Czy kupon zostal wykorzystany (0/1) |
|
||||
| date_used | Data wykorzystania kuponu (NULL gdy brak) |
|
||||
| used_count | Licznik uzyc kuponu |
|
||||
| type | Typ kuponu (obecnie: 1 = rabat procentowy na koszyk) |
|
||||
| amount | Wartosc kuponu (np. procent) |
|
||||
| one_time | Czy kupon jednorazowy (0/1) |
|
||||
| include_discounted_product | Czy obejmuje rowniez produkty przecenione (0/1) |
|
||||
| categories | JSON z ID kategorii objetych kuponem (NULL = bez ograniczenia) |
|
||||
|
||||
**Uzywane w:** `Domain\Coupon\CouponRepository`, `admin\Controllers\ShopCouponController`, `shop\Coupon`, `front\factory\ShopCoupon`, `front\factory\ShopOrder`
|
||||
|
||||
**Aktualizacja 2026-02-13 (ver. 0.266):** modul `/admin/shop_coupon` korzysta z `Domain\Coupon\CouponRepository` przez `admin\Controllers\ShopCouponController`. Usunieto legacy klasy `admin\controls\ShopCoupon` i `admin\factory\ShopCoupon`.
|
||||
|
||||
## pp_shop_promotion
|
||||
Promocje sklepu (modul `/admin/shop_promotion`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa promocji |
|
||||
| status | Status: 1 = aktywna, 0 = nieaktywna |
|
||||
| condition_type | Typ warunku promocji (slownik w `shop\Promotion::$condition_type`) |
|
||||
| discount_type | Typ rabatu (slownik w `shop\Promotion::$discount_type`) |
|
||||
| amount | Wartosc rabatu (np. procent) |
|
||||
| date_from | Data startu promocji (NULL = aktywna od razu) |
|
||||
| date_to | Data konca promocji (NULL = bez daty konca) |
|
||||
| categories | JSON z ID kategorii grupy I |
|
||||
| condition_categories | JSON z ID kategorii grupy II |
|
||||
| include_coupon | Czy laczyc z kuponami rabatowymi (0/1) |
|
||||
| include_product_promo | Czy uwzgledniac produkty przecenione (0/1) |
|
||||
| min_product_count | Minimalna liczba produktow (dla wybranych warunkow) |
|
||||
| price_cheapest_product | Cena najtanszego produktu (dla wybranych warunkow) |
|
||||
|
||||
**Uzywane w:** `Domain\Promotion\PromotionRepository`, `admin\Controllers\ShopPromotionController`, `shop\Promotion`, `front\factory\ShopPromotion`
|
||||
|
||||
**Aktualizacja 2026-02-13:** modul `/admin/shop_promotion` korzysta z `Domain\Promotion\PromotionRepository` przez `admin\Controllers\ShopPromotionController`. Usunieto legacy klasy `admin\controls\ShopPromotion` i `admin\factory\ShopPromotion`.
|
||||
|
||||
**Aktualizacja 2026-02-13 (ver. 0.265):** dodano obsluge `date_from` (repozytorium, formularz admin, lista admin, filtr aktywnych promocji na froncie) oraz poprawke zapisu edycji promocji po `id`.
|
||||
|
||||
## pp_shop_payment_methods
|
||||
Metody platnosci sklepu (modul `/admin/shop_payment_method`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa metody platnosci |
|
||||
| description | Opis metody platnosci (wyswietlany m.in. w checkout) |
|
||||
| status | Status: 1 = aktywna, 0 = nieaktywna |
|
||||
| apilo_payment_type_id | ID typu platnosci Apilo (NULL gdy brak mapowania) |
|
||||
| sellasist_payment_type_id | DEPRECATED (integracja Sellasist usunieta w ver. 0.263) |
|
||||
|
||||
**Uzywane w:** `Domain\PaymentMethod\PaymentMethodRepository`, `admin\Controllers\ShopPaymentMethodController`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod`, `admin\controls\ShopTransport`, `cron.php`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.268):** modul `/admin/shop_payment_method` korzysta z `Domain\PaymentMethod\PaymentMethodRepository` przez `admin\Controllers\ShopPaymentMethodController`. Usunieto legacy klasy `admin\controls\ShopPaymentMethod`, `admin\factory\ShopPaymentMethod`, `admin\view\ShopPaymentMethod` oraz widok `admin/templates/shop-payment-method/view-list.php`.
|
||||
|
||||
## pp_shop_transports
|
||||
Rodzaje transportu sklepu (modul `/admin/shop_transport`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa (systemowa, readonly) |
|
||||
| name_visible | Nazwa widoczna dla klienta |
|
||||
| description | Opis metody transportu |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| cost | Koszt dostawy (PLN) |
|
||||
| max_wp | Maksymalna waga paczki (NULL = bez limitu) |
|
||||
| default | Domyslna forma dostawy (0/1) |
|
||||
| delivery_free | Czy obsluguje darmowa dostawe (0/1) |
|
||||
| apilo_carrier_account_id | ID konta przewoznika w Apilo (NULL gdy brak mapowania) |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
|
||||
**Uzywane w:** `Domain\Transport\TransportRepository`, `admin\Controllers\ShopTransportController`, `front\factory\ShopTransport`
|
||||
|
||||
## pp_shop_transport_payment_methods
|
||||
Powiazanie metod transportu z metodami platnosci (tabela lacznikowa).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id_transport | FK do pp_shop_transports |
|
||||
| id_payment_method | FK do pp_shop_payment_methods |
|
||||
|
||||
**Uzywane w:** `Domain\Transport\TransportRepository`, `Domain\PaymentMethod\PaymentMethodRepository::forTransport()`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.269):** modul `/admin/shop_transport` korzysta z `Domain\Transport\TransportRepository` przez `admin\Controllers\ShopTransportController`. Usunieto legacy klasy `admin\controls\ShopTransport`, `admin\view\ShopTransport` oraz widok `admin/templates/shop-transport/view-list.php`.
|
||||
|
||||
## pp_shop_apilo_settings
|
||||
Ustawienia integracji Apilo (key-value).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Klucz ustawienia (np. client-id, access-token) |
|
||||
| value | Wartosc ustawienia |
|
||||
|
||||
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
|
||||
|
||||
## pp_shop_shoppro_settings
|
||||
Ustawienia integracji ShopPRO (key-value).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Klucz ustawienia (np. domain, db_name) |
|
||||
| value | Wartosc ustawienia |
|
||||
|
||||
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
|
||||
|
||||
**Aktualizacja 2026-02-13:** modul `/admin/integrations/` korzysta z `Domain\Integrations\IntegrationsRepository` (DI kontroler + fasada legacy). Usunieto integracje Sellasist i Baselinker.
|
||||
|
||||
## pp_shop_statuses
|
||||
Statusy zamowien sklepu (modul `/admin/shop_statuses`). Statusy sa predefiniowane - brak dodawania/usuwania, mozliwa edycja koloru i mapowania Apilo.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK (zaczyna sie od 0!) |
|
||||
| status | Nazwa statusu (read-only) |
|
||||
| color | Kolor statusu (hex, np. #ff0000) |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
| apilo_status_id | ID statusu w Apilo (NULL gdy brak mapowania) |
|
||||
| baselinker_status_id | DEPRECATED (usuniety w ver. 0.263) |
|
||||
| sellasist_status_id | DEPRECATED (usuniety w ver. 0.263) |
|
||||
|
||||
**Uzywane w:** `Domain\ShopStatus\ShopStatusRepository`, `admin\Controllers\ShopStatusesController`, `front\factory\ShopStatuses`, `shop\Order`, `cron.php`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.267):** modul `/admin/shop_statuses` korzysta z `Domain\ShopStatus\ShopStatusRepository` przez `admin\Controllers\ShopStatusesController`. Usunieto legacy klasy `admin\controls\ShopStatuses` i `admin\factory\ShopStatuses`. `front\factory\ShopStatuses` dziala jako fasada do repozytorium.
|
||||
|
||||
## pp_shop_product_sets
|
||||
Komplety produktow (modul `/admin/shop_product_sets`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa kompletu |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
|
||||
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `admin\Controllers\ShopProductSetsController`, `shop\ProductSet`, `shop\Product`
|
||||
|
||||
## pp_shop_product_sets_products
|
||||
Powiazanie kompletow z produktami (tabela lacznikowa).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| set_id | FK do pp_shop_product_sets |
|
||||
| product_id | FK do pp_shop_products |
|
||||
|
||||
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `shop\Product`, `front\factory\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.272):** modul `/admin/shop_product_sets` korzysta z `Domain\ProductSet\ProductSetRepository` przez `admin\Controllers\ShopProductSetsController`. Usunieto legacy klasy `admin\controls\ShopProductSets` i `admin\factory\ShopProductSet`. `shop\ProductSet` dziala jako fasada do repozytorium.
|
||||
|
||||
## pp_shop_producer
|
||||
Producenci produktow (modul `/admin/shop_producer`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa producenta |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| img | Sciezka do logo producenta (NULL gdy brak) |
|
||||
|
||||
**Uzywane w:** `Domain\Producer\ProducerRepository`, `admin\Controllers\ShopProducerController`, `shop\Producer`, `shop\Product`, `front\controls\ShopProducer`
|
||||
|
||||
## pp_shop_producer_lang
|
||||
Tlumaczenia producentow (per jezyk). FK kaskadowe ON DELETE CASCADE.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| producer_id | FK do pp_shop_producer |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| description | Opis producenta (TEXT) |
|
||||
| data | Dane producenta (TEXT, HTML) |
|
||||
| meta_title | Meta title SEO (VARCHAR 255) |
|
||||
|
||||
**Uzywane w:** `Domain\Producer\ProducerRepository`, `shop\Producer`, `shop\Product`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.273):** modul `/admin/shop_producer` korzysta z `Domain\Producer\ProducerRepository` przez `admin\Controllers\ShopProducerController`. Usunieto legacy `admin\controls\ShopProducer` i `admin\factory\ShopProducer`. `shop\Producer` dziala jako fasada do repozytorium.
|
||||
338
temp/update_build/tmp_0.275/docs/PROJECT_STRUCTURE.md
Normal file
338
temp/update_build/tmp_0.275/docs/PROJECT_STRUCTURE.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Struktura Projektu shopPRO
|
||||
|
||||
Dokumentacja struktury projektu shopPRO do szybkiego odniesienia.
|
||||
|
||||
## System Cache (Redis)
|
||||
|
||||
### Klasy odpowiedzialne za cache
|
||||
|
||||
#### RedisConnection
|
||||
- **Plik:** `autoload/class.RedisConnection.php`
|
||||
- **Opis:** Singleton zarządzający połączeniem z Redis
|
||||
- **Metody:**
|
||||
- `getInstance()` - pobiera instancję połączenia
|
||||
- `getConnection()` - zwraca obiekt Redis
|
||||
|
||||
#### CacheHandler
|
||||
- **Plik:** `autoload/class.CacheHandler.php`
|
||||
- **Opis:** Handler do obsługi cache Redis
|
||||
- **Metody:**
|
||||
- `get($key)` - pobiera wartość z cache
|
||||
- `set($key, $value, $ttl = 86400)` - zapisuje wartość do cache
|
||||
- `exists($key)` - sprawdza czy klucz istnieje
|
||||
- `delete($key)` - usuwa pojedynczy klucz
|
||||
- `deletePattern($pattern)` - usuwa klucze według wzorca
|
||||
|
||||
#### Klasa S (pomocnicza)
|
||||
- **Plik:** `autoload/class.S.php`
|
||||
- **Metody cache:**
|
||||
- `clear_redis_cache()` - czyści cały cache Redis (flushAll)
|
||||
- `clear_product_cache(int $product_id)` - czyści cache konkretnego produktu
|
||||
|
||||
### Wzorce kluczy Redis
|
||||
|
||||
#### Produkty
|
||||
```
|
||||
shop\product:{product_id}:{lang_id}:{permutation_hash}
|
||||
```
|
||||
- Przechowuje zserializowany obiekt produktu
|
||||
- TTL: 24 godziny (86400 sekund)
|
||||
- Klasa: `shop\Product::getFromCache()` - `autoload/shop/class.Product.php:121`
|
||||
|
||||
#### Opcje ilościowe produktu
|
||||
```
|
||||
\shop\Product::get_product_permutation_quantity_options:{product_id}:{permutation}
|
||||
```
|
||||
- Przechowuje informacje o ilości i komunikatach magazynowych
|
||||
- Klasa: `shop\Product::get_product_permutation_quantity_options()` - `autoload/shop/class.Product.php:549`
|
||||
|
||||
#### Zestawy produktów
|
||||
```
|
||||
\shop\Product::product_sets_when_add_to_basket:{product_id}
|
||||
```
|
||||
- Przechowuje produkty często kupowane razem
|
||||
- Klasa: `shop\Product::product_sets_when_add_to_basket()` - `autoload/shop/class.Product.php:316`
|
||||
|
||||
## Integracje z systemami zewnętrznymi (CRON)
|
||||
|
||||
### Plik: `cron.php`
|
||||
|
||||
#### Apilo
|
||||
- **Aktualizacja pojedynczego produktu:** synchronizacja cen i stanow
|
||||
- Czestotliwosc: Co 10 minut
|
||||
- **Synchronizacja cennika:** masowa aktualizacja cen z Apilo
|
||||
- Czestotliwosc: Co 1 godzine
|
||||
- **Synchronizacja zaleglych syncow platnosci/statusow:** kolejka retry dla chwilowej niedostepnosci Apilo (`temp/apilo-sync-queue.json`)
|
||||
- Przetwarzanie: przy kazdym uruchomieniu `cron.php` (limit wsadowy)
|
||||
|
||||
**Uwaga:** Integracje Sellasist i Baselinker zostaly usuniete w ver. 0.263.
|
||||
|
||||
## Panel Administratora
|
||||
|
||||
### Routing
|
||||
- Główny katalog: `admin/`
|
||||
- Template główny: `admin/templates/site/main-layout.php`
|
||||
- Kontrolery (nowe): `autoload/admin/Controllers/`
|
||||
- Kontrolery legacy (fallback): `autoload/admin/controls/`
|
||||
|
||||
### Przycisk "Wyczyść cache"
|
||||
- **Lokalizacja UI:** `admin/templates/site/main-layout.php:172`
|
||||
- **JavaScript:** `admin/templates/site/main-layout.php:235-274`
|
||||
- **Endpoint AJAX:** `/admin/settings/clear_cache_ajax/`
|
||||
- **Kontroler:** `autoload/admin/Controllers/SettingsController.php:43-60`
|
||||
- **Działanie:**
|
||||
1. Pokazuje spinner "Czyszczę cache..."
|
||||
2. Czyści katalogi: `temp/`, `thumbs/`
|
||||
3. Wykonuje `flushAll()` na Redis
|
||||
4. Pokazuje "Cache wyczyszczony!" przez 2 sekundy
|
||||
5. Przywraca stan początkowy
|
||||
|
||||
## Struktura katalogów
|
||||
|
||||
```
|
||||
shopPRO/
|
||||
├── admin/ # Panel administratora
|
||||
│ ├── templates/ # Szablony widoków
|
||||
│ └── layout/ # Zasoby CSS/JS/ikony
|
||||
├── autoload/ # Klasy autoloadowane
|
||||
│ ├── admin/ # Klasy panelu admin
|
||||
│ │ ├── Controllers/ # Nowe kontrolery DI
|
||||
│ │ ├── controls/ # Kontrolery legacy (fallback)
|
||||
│ │ └── factory/ # Fabryki/helpery
|
||||
│ ├── Domain/ # Repozytoria/logika domenowa
|
||||
│ ├── front/ # Klasy frontendu
|
||||
│ │ └── factory/ # Fabryki/helpery
|
||||
│ └── shop/ # Klasy sklepu
|
||||
├── docs/ # Dokumentacja techniczna
|
||||
├── libraries/ # Biblioteki zewnętrzne
|
||||
├── temp/ # Cache tymczasowy
|
||||
├── thumbs/ # Miniatury zdjęć
|
||||
└── cron.php # Zadania CRON
|
||||
```
|
||||
|
||||
## Baza danych
|
||||
|
||||
### Główne tabele produktów
|
||||
- `pp_shop_products` - produkty główne
|
||||
- `pp_shop_products_langs` - tłumaczenia produktów
|
||||
- `pp_shop_products_images` - zdjęcia produktów
|
||||
- `pp_shop_products_categories` - kategorie produktów
|
||||
- `pp_shop_products_custom_fields` - pola własne produktów
|
||||
|
||||
### Tabele integracji
|
||||
- Kolumny w `pp_shop_products`:
|
||||
- `apilo_product_id`, `apilo_product_name`, `apilo_get_data_date`
|
||||
- Tabele ustawien:
|
||||
- `pp_shop_apilo_settings` (key-value)
|
||||
- `pp_shop_shoppro_settings` (key-value)
|
||||
|
||||
### Tabele checkout
|
||||
- `pp_shop_payment_methods` - metody platnosci sklepu (mapowanie `apilo_payment_type_id`)
|
||||
- `pp_shop_transports` - rodzaje transportu sklepu (mapowanie `apilo_carrier_account_id`)
|
||||
- `pp_shop_transport_payment_methods` - powiazanie metod transportu i platnosci
|
||||
|
||||
Pelna dokumentacja tabel: `DATABASE_STRUCTURE.md`
|
||||
|
||||
## Konfiguracja
|
||||
|
||||
### Redis
|
||||
- Konfiguracja: `config.php` (zmienna `$config['redis']`)
|
||||
- Parametry: host, port, password
|
||||
|
||||
### Autoload
|
||||
- Funkcja: `__autoload_my_classes()` w `cron.php:6`
|
||||
- Wzorzec: `autoload/{namespace}/class.{ClassName}.php`
|
||||
|
||||
## Klasy pomocnicze
|
||||
|
||||
### \S (autoload/class.S.php)
|
||||
Główna klasa helper z metodami:
|
||||
- `seo($val)` - generowanie URL SEO
|
||||
- `normalize_decimal($val, $precision)` - normalizacja liczb
|
||||
- `send_email()` - wysyłanie emaili
|
||||
- `delete_dir($dir)` - usuwanie katalogów
|
||||
- `htacces()` - generowanie .htaccess i sitemap.xml
|
||||
|
||||
### Medoo
|
||||
- Plik: `libraries/medoo/medoo.php`
|
||||
- Zmienna: `$mdb`
|
||||
- ORM do operacji na bazie danych
|
||||
|
||||
## Najważniejsze wzorce
|
||||
|
||||
### Namespace'y
|
||||
- `\admin\Controllers\` - nowe kontrolery panelu admin (DI)
|
||||
- `\admin\controls\` - kontrolery legacy (fallback)
|
||||
- `\Domain\` - repozytoria/logika domenowa
|
||||
- `\admin\factory\` - helpery/fabryki admin
|
||||
- `\front\factory\` - helpery/fabryki frontend
|
||||
- `\shop\` - klasy sklepu (Product, Order, itp.)
|
||||
|
||||
### Cachowanie produktów
|
||||
```php
|
||||
// Pobranie produktu z cache
|
||||
$product = \shop\Product::getFromCache($product_id, $lang_id, $permutation_hash);
|
||||
|
||||
// Czyszczenie cache produktu
|
||||
\S::clear_product_cache($product_id);
|
||||
|
||||
// Czyszczenie całego cache
|
||||
\S::clear_redis_cache();
|
||||
```
|
||||
|
||||
## Refaktoryzacja do Domain-Driven Architecture
|
||||
|
||||
### Nowa struktura (w trakcie migracji)
|
||||
```
|
||||
autoload/
|
||||
├── Domain/ # Nowa warstwa biznesowa (namespace \Domain\)
|
||||
│ ├── Product/
|
||||
│ │ └── ProductRepository.php
|
||||
│ ├── Banner/
|
||||
│ │ └── BannerRepository.php
|
||||
│ ├── Settings/
|
||||
│ │ └── SettingsRepository.php
|
||||
│ ├── Cache/
|
||||
│ │ └── CacheRepository.php
|
||||
│ ├── Article/
|
||||
│ │ └── ArticleRepository.php
|
||||
│ ├── User/
|
||||
│ │ └── UserRepository.php
|
||||
│ ├── Languages/
|
||||
│ │ └── LanguagesRepository.php
|
||||
│ ├── Layouts/
|
||||
│ │ └── LayoutsRepository.php
|
||||
│ ├── Newsletter/
|
||||
│ │ └── NewsletterRepository.php
|
||||
│ ├── Scontainers/
|
||||
│ │ └── ScontainersRepository.php
|
||||
│ ├── Dictionaries/
|
||||
│ │ └── DictionariesRepository.php
|
||||
│ ├── Pages/
|
||||
│ │ └── PagesRepository.php
|
||||
│ ├── Integrations/
|
||||
│ │ └── IntegrationsRepository.php
|
||||
│ ├── Promotion/
|
||||
│ │ └── PromotionRepository.php
|
||||
│ ├── Coupon/
|
||||
│ │ └── CouponRepository.php
|
||||
│ ├── ShopStatus/
|
||||
│ │ └── ShopStatusRepository.php
|
||||
│ ├── Transport/
|
||||
│ │ └── TransportRepository.php
|
||||
│ ├── ProductSet/
|
||||
│ │ └── ProductSetRepository.php
|
||||
│ ├── Producer/
|
||||
│ │ └── ProducerRepository.php
|
||||
│ └── ...
|
||||
├── admin/
|
||||
│ ├── Controllers/ # Nowe kontrolery (namespace \admin\Controllers\)
|
||||
│ ├── class.Site.php # Router: nowy kontroler → fallback stary
|
||||
│ ├── controls/ # Stare kontrolery (niezależny fallback)
|
||||
│ ├── factory/ # Stare helpery (niezależny fallback)
|
||||
│ └── view/ # Widoki (statyczne - bez zmian)
|
||||
├── shop/ # Legacy - fasady do Domain
|
||||
└── front/factory/ # Legacy - stopniowo migrowane
|
||||
```
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.268):**
|
||||
- Dodano modul domenowy `Domain/PaymentMethod/PaymentMethodRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopPaymentMethodController.php`.
|
||||
- Modul `/admin/shop_payment_method/*` dziala na nowych widokach (`payment-methods-list`, `payment-method-edit`).
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopPaymentMethod.php`, `autoload/admin/factory/class.ShopPaymentMethod.php`, `autoload/admin/view/class.ShopPaymentMethod.php`, `admin/templates/shop-payment-method/view-list.php`.
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.269):**
|
||||
- Dodano modul domenowy `Domain/Transport/TransportRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopTransportController.php`.
|
||||
- Modul `/admin/shop_transport/*` dziala na nowych widokach (`transports-list`, `transport-edit`).
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopTransport.php`, `autoload/admin/view/class.ShopTransport.php`, `admin/templates/shop-transport/view-list.php`.
|
||||
- `admin\factory\ShopTransport` i `front\factory\ShopTransport` przepiete na repozytorium.
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.270):**
|
||||
- `shop\Order` zapisuje nieudane syncy Apilo (status/platnosc) do kolejki `temp/apilo-sync-queue.json`.
|
||||
- `cron.php` automatycznie ponawia zalegle syncy (`Order::process_apilo_sync_queue()`).
|
||||
- `shop\Order::set_as_paid()` wysyla mapowany typ platnosci Apilo (z mapowania metody platnosci), bez stalej wartosci `type`.
|
||||
|
||||
### Routing admin (admin\Site::route())
|
||||
1. Sprawdź mapę `$newControllers` → utwórz instancję z DI → wywołaj
|
||||
2. Jeśli nowy kontroler nie istnieje (`class_exists()` = false) → fallback na `admin\controls\`
|
||||
3. Stary kontroler jest NIEZALEŻNY od nowych klas (bezpieczny fallback)
|
||||
|
||||
### Dependency Injection
|
||||
Nowe klasy używają **Dependency Injection** zamiast `global` variables:
|
||||
```php
|
||||
// STARE
|
||||
global $mdb;
|
||||
$quantity = $mdb->get('pp_shop_products', 'quantity', ['id' => $id]);
|
||||
|
||||
// NOWE
|
||||
$repository = new \Domain\Product\ProductRepository($mdb);
|
||||
$quantity = $repository->getQuantity($id);
|
||||
```
|
||||
|
||||
## Testowanie (tylko dla deweloperów)
|
||||
|
||||
**UWAGA:** Pliki testów NIE są częścią aktualizacji dla klientów!
|
||||
|
||||
### Narzędzia
|
||||
- **PHPUnit 9.6.34** - framework testowy
|
||||
- **test.bat** - uruchamianie testów
|
||||
- **composer.json** - autoloading PSR-4
|
||||
|
||||
Pelna dokumentacja testow: `TESTING.md`
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-14 (ver. 0.271)
|
||||
- Dodano modul domenowy `Domain/Attribute/AttributeRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopAttributeController.php`.
|
||||
- Modul `/admin/shop_attribute/*` zostal przepiety na nowe widoki (`attributes-list`, `attribute-edit`, `values-edit`).
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopAttribute.php`, `autoload/admin/factory/class.ShopAttribute.php`, `autoload/admin/view/class.ShopAttribute.php`, `admin/templates/shop-attribute/_partials/value.php`.
|
||||
- Przepieto zaleznosci kombinacji produktu na `Domain\Attribute\AttributeRepository` i `shop\ProductAttribute`.
|
||||
- Dla `ShopAttribute` routing celowo nie wykonuje fallbacku akcji do legacy kontrolera.
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-15 (ver. 0.272)
|
||||
- Dodano modul domenowy `Domain/ProductSet/ProductSetRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopProductSetsController.php`.
|
||||
- Modul `/admin/shop_product_sets/*` dziala na nowych widokach (`product-sets-list`, `product-set-edit`).
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopProductSets.php`, `autoload/admin/factory/class.ShopProductSet.php`, `admin/templates/shop-product-sets/view-list.php`, `admin/templates/shop-product-sets/set-edit.php`.
|
||||
- `shop\ProductSet` przepiety na fasade do `Domain\ProductSet\ProductSetRepository`.
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-15 (ver. 0.273)
|
||||
- Dodano modul domenowy `Domain/Producer/ProducerRepository.php`.
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-15 (ver. 0.274)
|
||||
- Dodano modul domenowy `Domain/Client/ClientRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopClientsController.php`.
|
||||
- Modul `/admin/shop_clients/*` dziala na nowych widokach opartych o `components/table-list`.
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopClients.php`, `autoload/admin/factory/class.ShopClients.php`.
|
||||
- Routing i menu admin przepiete na kanoniczny URL `/admin/shop_clients/list/`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopProducerController.php`.
|
||||
- Modul `/admin/shop_producer/*` dziala na nowych widokach (`producers-list`, `producer-edit`).
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopProducer.php`, `admin/templates/shop-producer/list.php`, `admin/templates/shop-producer/edit.php`.
|
||||
- `shop\Producer` przepiety na fasade do `Domain\Producer\ProducerRepository`.
|
||||
- `admin\controls\ShopProduct` uzywa `ProducerRepository::allProducers()`.
|
||||
- Usunieto 6 pustych factory facades: `admin\factory\Languages`, `admin\factory\Newsletter`, `admin\factory\Scontainers`, `admin\factory\ShopProducer`, `admin\factory\ShopTransport`, `admin\factory\Layouts`.
|
||||
- Przepieto 2 wywolania `admin\factory\ShopTransport` w `admin\factory\ShopProduct` na `Domain\Transport\TransportRepository`.
|
||||
- Usuniety fallback do `admin\factory\Layouts` w `admin\controls\ShopProduct`.
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-15 (ver. 0.274)
|
||||
- Dodano kontroler DI `admin/Controllers/ShopProductController.php` (akcje `mass_edit`, `mass_edit_save`, `get_products_by_category`).
|
||||
- Routing `admin\Site` rozszerzono o mapowanie `ShopProduct` do nowego kontrolera.
|
||||
- `Domain/Product/ProductRepository.php` rozszerzono o metody dla mass-edit: `allProductsForMassEdit`, `getProductsByCategory`, `applyDiscountPercent`.
|
||||
- Usunieto legacy akcje mass-edit z `autoload/admin/controls/class.ShopProduct.php`.
|
||||
- Widok `/admin/shop_product/mass_edit/` przepiety na nowy partial `admin/templates/shop-product/mass-edit-custom-script.php`.
|
||||
- Ujednolicono UI drzewek (strzalki/expand) w:
|
||||
- `admin/templates/pages/pages-list.php` + `admin/templates/pages/subpages-list.php`
|
||||
- `admin/templates/articles/subpages-list.php` + `admin/templates/articles/article-edit-custom-script.php`
|
||||
|
||||
## Dodatkowa aktualizacja 2026-02-15 (ver. 0.275)
|
||||
- Dodano modul domenowy `Domain/Category/CategoryRepository.php`.
|
||||
- Dodano kontroler DI `admin/Controllers/ShopCategoryController.php`.
|
||||
- Modul `/admin/shop_category/*` dziala przez DI i kanoniczny URL `/admin/shop_category/list/` (z zachowaniem aliasu `view_list`).
|
||||
- Widoki `shop-category/*` maja wydzielone skrypty `*-custom-script.php` i ujednolicone strzalki drzewa (`button + caret + aria-expanded`).
|
||||
- Endpointy AJAX dla drzewka kategorii i kolejnosci produktow przepiete na `/admin/shop_category/save_categories_order/`, `/admin/shop_category/save_products_order/`, `/admin/shop_category/cookie_categories/`.
|
||||
- Usunieto legacy: `autoload/admin/controls/class.ShopCategory.php`, `autoload/admin/factory/class.ShopCategory.php`, `autoload/admin/view/class.ShopCategory.php`.
|
||||
- Przepieto zaleznosci `ShopProduct` z `admin\factory\ShopCategory` na `Domain\Category\CategoryRepository`.
|
||||
- Usunieto preload `autoload/admin/factory/class.ShopCategory.php` z `libraries/grid/config.php`.
|
||||
|
||||
---
|
||||
*Dokument aktualizowany: 2026-02-15*
|
||||
297
temp/update_build/tmp_0.275/docs/REFACTORING_PLAN.md
Normal file
297
temp/update_build/tmp_0.275/docs/REFACTORING_PLAN.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Plan Refaktoryzacji shopPRO - Domain-Driven Architecture
|
||||
|
||||
## Cel
|
||||
Stopniowe przeniesienie logiki biznesowej do architektury warstwowej:
|
||||
- **Domain/** - logika biznesowa (core)
|
||||
- **Admin/** - warstwa administratora
|
||||
- **Frontend/** - warstwa użytkownika
|
||||
- **Shared/** - współdzielone narzędzia
|
||||
|
||||
## Docelowa struktura
|
||||
|
||||
```
|
||||
autoload/
|
||||
├── Domain/ # Logika biznesowa (CORE) - namespace \Domain\
|
||||
│ ├── Product/
|
||||
│ │ ├── ProductRepository.php
|
||||
│ │ ├── ProductService.php # (przyszłość)
|
||||
│ │ └── ProductCacheService.php # (przyszłość)
|
||||
│ ├── Banner/
|
||||
│ │ └── BannerRepository.php
|
||||
│ ├── Settings/
|
||||
│ │ └── SettingsRepository.php
|
||||
│ ├── Cache/
|
||||
│ │ └── CacheRepository.php
|
||||
│ ├── Order/
|
||||
│ ├── Category/
|
||||
│ └── ...
|
||||
│
|
||||
├── admin/ # Warstwa administratora (istniejący katalog!)
|
||||
│ ├── Controllers/ # Nowe kontrolery - namespace \admin\Controllers\
|
||||
│ ├── controls/ # Stare kontrolery (legacy fallback)
|
||||
│ ├── factory/ # Stare helpery (legacy)
|
||||
│ └── view/ # Widoki (statyczne - OK bez zmian)
|
||||
│
|
||||
├── Frontend/ # Warstwa użytkownika (przyszłość)
|
||||
│ ├── Controllers/
|
||||
│ └── Services/
|
||||
│
|
||||
├── Shared/ # Współdzielone narzędzia
|
||||
│ ├── Cache/
|
||||
│ │ ├── CacheHandler.php
|
||||
│ │ └── RedisConnection.php
|
||||
│ └── Helpers/
|
||||
│ └── S.php
|
||||
│
|
||||
└── [LEGACY] # Stare klasy (stopniowo deprecated)
|
||||
├── shop/
|
||||
├── admin/factory/
|
||||
└── front/factory/
|
||||
```
|
||||
|
||||
### WAŻNE: Konwencja namespace → katalog (Linux case-sensitive!)
|
||||
- `\Domain\` → `autoload/Domain/` (duże D - nowy katalog)
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (małe a - istniejący katalog)
|
||||
- NIE używać `\Admin\` (duże A) bo na serwerze Linux katalog to `admin/` (małe a)
|
||||
|
||||
## Zasady migracji
|
||||
|
||||
### 1. Stopniowość
|
||||
- Przenosimy **jedną funkcję na raz**
|
||||
- Zachowujemy kompatybilność wsteczną
|
||||
- Stare klasy działają jako fasady do nowych
|
||||
|
||||
### 2. Dependency Injection zamiast statycznych metod
|
||||
```php
|
||||
// ❌ STARE - statyczne
|
||||
class Product {
|
||||
public static function getQuantity($id) {
|
||||
global $mdb;
|
||||
return $mdb->get('pp_shop_products', 'quantity', ['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ NOWE - instancje z DI
|
||||
class ProductRepository {
|
||||
private $db;
|
||||
|
||||
public function __construct($db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getQuantity($id) {
|
||||
return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Fasady dla kompatybilności
|
||||
```php
|
||||
// Stara klasa wywołuje nową
|
||||
namespace shop;
|
||||
|
||||
class Product {
|
||||
public static function getQuantity($id) {
|
||||
global $mdb;
|
||||
$repo = new \Domain\Product\ProductRepository($mdb);
|
||||
return $repo->getQuantity($id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Proces migracji funkcji
|
||||
|
||||
### Krok 1: Wybór funkcji
|
||||
- Wybierz prostą funkcję statyczną
|
||||
- Sprawdź jej zależności
|
||||
- Przeanalizuj gdzie jest używana
|
||||
|
||||
### Krok 2: Stworzenie nowej struktury
|
||||
- Utwórz folder `Domain/{Module}/`
|
||||
- Stwórz odpowiednią klasę (Repository/Service/Entity)
|
||||
- Przenieś logikę
|
||||
|
||||
### Krok 3: Znalezienie użyć
|
||||
```bash
|
||||
grep -r "Product::getQuantity" .
|
||||
```
|
||||
|
||||
### Krok 4: Aktualizacja wywołań
|
||||
- Opcja A: Bezpośrednie wywołanie nowej klasy
|
||||
- Opcja B: Fasada w starej klasie (zalecane na początek)
|
||||
|
||||
### Krok 5: Testy
|
||||
- Napisz test jednostkowy dla nowej funkcji
|
||||
- Sprawdź czy stare wywołania działają
|
||||
|
||||
## Status migracji
|
||||
|
||||
### ✅ Zmigrowane moduły
|
||||
| # | Modul | Wersja | Zakres |
|
||||
|---|-------|--------|--------|
|
||||
| 1 | Cache | 0.237 | CacheHandler, RedisConnection, clear_product_cache |
|
||||
| 2 | Product | 0.238-0.252, 0.274 | getQuantity, getPrice, getName, archive/unarchive, allProductsForMassEdit, getProductsByCategory, applyDiscountPercent |
|
||||
| 3 | Banner | 0.239 | find, delete, save, kontroler DI |
|
||||
| 4 | Settings | 0.240/0.250 | saveSettings, getSettings, kontroler DI |
|
||||
| 5 | Dictionaries | 0.251 | listForAdmin, find, save, delete, kontroler DI |
|
||||
| 6 | ProductArchive | 0.252 | kontroler DI, table-list |
|
||||
| 7 | Filemanager | 0.252 | kontroler DI, fix Invalid Key |
|
||||
| 8 | Users | 0.253 | CRUD, logon, 2FA, kontroler DI |
|
||||
| 9 | Languages | 0.254 | languages + translations, kontroler DI |
|
||||
| 10 | Layouts | 0.256 | find, save, delete, menusWithPages, categoriesTree |
|
||||
| 11 | Newsletter | 0.257-0.258 | subskrybenci, szablony, ustawienia |
|
||||
| 12 | Scontainers | 0.259 | listForAdmin, find, save, delete |
|
||||
| 13 | ArticlesArchive | 0.260 | restore, deletePermanently |
|
||||
| 14 | Articles | 0.261 | pelna migracja (CRUD, AJAX, galeria, pliki) |
|
||||
| 15 | Pages | 0.262 | menu/page CRUD, drzewo stron, AJAX |
|
||||
| 16 | Integrations | 0.263 | Apilo/ShopPRO, cleanup Sellasist/Baselinker |
|
||||
| 17 | ShopPromotion | 0.264-0.265 | listForAdmin, find, save, delete, categoriesTree |
|
||||
| 18 | ShopCoupon | 0.266 | listForAdmin, find, save, delete, categoriesTree |
|
||||
| 19 | ShopStatuses | 0.267 | listForAdmin, find, save, color picker |
|
||||
| 20 | ShopPaymentMethod | 0.268 | listForAdmin, find, save, allActive, mapowanie Apilo, DI kontroler |
|
||||
| 21 | ShopTransport | 0.269 | listForAdmin, find, save, allActive, allForAdmin, findActiveById, getTransportCost, lowestTransportPrice, getApiloCarrierAccountId, powiazanie z PaymentMethod, DI kontroler |
|
||||
| 22 | ShopAttribute | 0.271 | list/edit/save/delete/values, nowy edytor wartosci, cleanup legacy, przepiecie zaleznosci kombinacji |
|
||||
| 23 | ShopProductSets | 0.272 | listForAdmin, find, save, delete, allSets, allProductsMap, multi-select Selectize, DI kontroler |
|
||||
| 24 | ShopProducer | 0.273 | listForAdmin, find, save, delete, allProducers, producerProducts, fasada shop\Producer, DI kontroler |
|
||||
| 25 | ShopProduct (mass_edit) | 0.274 | DI kontroler + routing dla `mass_edit`, `mass_edit_save`, `get_products_by_category`, cleanup legacy akcji |
|
||||
| 26 | ShopClients | 0.274 | DI kontroler + routing dla `list/details`, nowe listy na `components/table-list`, cleanup legacy controls/factory |
|
||||
| 27 | ShopCategory | 0.275 | CategoryRepository + DI kontroler + routing, endpointy AJAX (`save_categories_order`, `save_products_order`, `cookie_categories`), cleanup legacy controls/factory/view |
|
||||
|
||||
### Product - szczegolowy status
|
||||
- ✅ getQuantity (ver. 0.238)
|
||||
- ✅ getPrice (ver. 0.239)
|
||||
- ✅ getName (ver. 0.239)
|
||||
- ✅ archive / unarchive (ver. 0.241/0.252)
|
||||
- ✅ allProductsForMassEdit (ver. 0.274)
|
||||
- ✅ getProductsByCategory (ver. 0.274)
|
||||
- ✅ applyDiscountPercent (ver. 0.274)
|
||||
- [ ] is_product_on_promotion
|
||||
- [ ] getFromCache
|
||||
- [ ] getProductImg
|
||||
|
||||
### 📋 Do zrobienia
|
||||
- Order
|
||||
- ShopProduct (factory)
|
||||
|
||||
## Kolejność refaktoryzacji (priorytet)
|
||||
|
||||
1-27: ✅ Cache, Product, Banner, Settings, Dictionaries, ProductArchive, Filemanager, Users, Pages, Integrations, ShopPromotion, ShopCoupon, ShopStatuses, ShopPaymentMethod, ShopTransport, ShopAttribute, ShopProductSets, ShopProducer, ShopProduct (mass_edit), ShopClients, ShopCategory
|
||||
|
||||
Nastepne:
|
||||
28. **Order**
|
||||
|
||||
## Form Edit System
|
||||
|
||||
Nowy uniwersalny system formularzy edycji:
|
||||
- ✅ Klasy ViewModel: `FormFieldType`, `FormField`, `FormTab`, `FormAction`, `FormEditViewModel`
|
||||
- ✅ Walidacja: `FormValidator` z obsługą reguł per pole i sekcje językowe
|
||||
- ✅ Persist: `FormRequestHandler` - zapamiętywanie danych przy błędzie walidacji
|
||||
- ✅ Renderer: `FormFieldRenderer` - renderowanie wszystkich typów pól
|
||||
- ✅ Szablon: `admin/templates/components/form-edit.php` - uniwersalny layout
|
||||
- Wspierane typy pól: text, number, email, password, date, datetime, switch, select, textarea, editor, image, file, hidden, lang_section, color
|
||||
- Obsługa zakładek (vertical) i sekcji językowych (horizontal)
|
||||
- **Do zrobienia**: Przerobić pozostałe kontrolery/formularze (Product, Category, Pages, itd.)
|
||||
|
||||
Pelna dokumentacja: `docs/FORM_EDIT_SYSTEM.md`
|
||||
|
||||
## Zasady kodu
|
||||
|
||||
### 1. SOLID Principles
|
||||
- **S**ingle Responsibility - jedna klasa = jedna odpowiedzialność
|
||||
- **O**pen/Closed - otwarty na rozszerzenia, zamknięty na modyfikacje
|
||||
- **L**iskov Substitution - podklasy mogą zastąpić nadklasy
|
||||
- **I**nterface Segregation - wiele małych interfejsów
|
||||
- **D**ependency Inversion - zależności od abstrakcji
|
||||
|
||||
### 2. Nazewnictwo
|
||||
- **Entity** - `Product.php` (reprezentuje obiekt domenowy)
|
||||
- **Repository** - `ProductRepository.php` (dostęp do danych)
|
||||
- **Service** - `ProductService.php` (logika biznesowa)
|
||||
- **Controller** - `ProductController.php` (obsługa requestów)
|
||||
|
||||
### 3. Type Hinting
|
||||
```php
|
||||
// ✅ DOBRE
|
||||
public function getQuantity(int $id): ?int {
|
||||
return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]);
|
||||
}
|
||||
|
||||
// ❌ ZŁE
|
||||
public function getQuantity($id) {
|
||||
return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]);
|
||||
}
|
||||
```
|
||||
|
||||
## Narzędzia pomocnicze
|
||||
|
||||
### Autoloader (produkcja)
|
||||
Autoloader w 9 entry pointach obsługuje dwie konwencje:
|
||||
1. `autoload/{namespace}/class.{ClassName}.php` (legacy)
|
||||
2. `autoload/{namespace}/{ClassName}.php` (PSR-4, fallback)
|
||||
|
||||
Entry pointy: `index.php`, `ajax.php`, `api.php`, `cron.php`, `cron-turstmate.php`, `download.php`, `admin/index.php`, `admin/ajax.php`, `cron/cron-xml.php`
|
||||
|
||||
### Static Analysis
|
||||
```bash
|
||||
composer require --dev phpstan/phpstan
|
||||
vendor/bin/phpstan analyse autoload/Domain
|
||||
```
|
||||
|
||||
## Testowanie
|
||||
|
||||
### Framework: PHPUnit
|
||||
```bash
|
||||
composer test
|
||||
```
|
||||
|
||||
### Struktura testów
|
||||
```
|
||||
tests/
|
||||
├── Unit/
|
||||
│ ├── Domain/
|
||||
│ │ ├── Article/ArticleRepositoryTest.php
|
||||
│ │ ├── Banner/BannerRepositoryTest.php
|
||||
│ │ ├── Cache/CacheRepositoryTest.php
|
||||
│ │ ├── Coupon/CouponRepositoryTest.php
|
||||
│ │ ├── Dictionaries/DictionariesRepositoryTest.php
|
||||
│ │ ├── Integrations/IntegrationsRepositoryTest.php
|
||||
│ │ ├── PaymentMethod/PaymentMethodRepositoryTest.php
|
||||
│ │ ├── Producer/ProducerRepositoryTest.php
|
||||
│ │ ├── Product/ProductRepositoryTest.php
|
||||
│ │ ├── ProductSet/ProductSetRepositoryTest.php
|
||||
│ │ ├── Promotion/PromotionRepositoryTest.php
|
||||
│ │ ├── Settings/SettingsRepositoryTest.php
|
||||
│ │ ├── ShopStatus/ShopStatusRepositoryTest.php
|
||||
│ │ └── User/UserRepositoryTest.php
|
||||
│ └── admin/
|
||||
│ └── Controllers/
|
||||
│ ├── ArticlesControllerTest.php
|
||||
│ ├── DictionariesControllerTest.php
|
||||
│ ├── IntegrationsControllerTest.php
|
||||
│ ├── ProductArchiveControllerTest.php
|
||||
│ ├── SettingsControllerTest.php
|
||||
│ ├── ShopCouponControllerTest.php
|
||||
│ ├── ShopPaymentMethodControllerTest.php
|
||||
│ ├── ShopProducerControllerTest.php
|
||||
│ ├── ShopProductSetsControllerTest.php
|
||||
│ ├── ShopPromotionControllerTest.php
|
||||
│ ├── ShopStatusesControllerTest.php
|
||||
│ └── UsersControllerTest.php
|
||||
└── Integration/
|
||||
```
|
||||
**Lacznie: 338 testow, 1063 asercji**
|
||||
|
||||
Aktualizacja 2026-02-15 (ver. 0.273):
|
||||
- dodano testy `tests/Unit/Domain/Producer/ProducerRepositoryTest.php`
|
||||
- dodano testy `tests/Unit/admin/Controllers/ShopProducerControllerTest.php`
|
||||
|
||||
Aktualizacja 2026-02-14 (ver. 0.271):
|
||||
- dodano testy `tests/Unit/Domain/Attribute/AttributeRepositoryTest.php`
|
||||
- dodano testy `tests/Unit/admin/Controllers/ShopAttributeControllerTest.php`
|
||||
|
||||
Pelna dokumentacja testow: `TESTING.md`
|
||||
|
||||
---
|
||||
*Rozpoczęto: 2025-02-05*
|
||||
*Ostatnia aktualizacja: 2026-02-15*
|
||||
*Changelog zmian: `docs/CHANGELOG.md`*
|
||||
456
temp/update_build/tmp_0.275/docs/TESTING.md
Normal file
456
temp/update_build/tmp_0.275/docs/TESTING.md
Normal file
@@ -0,0 +1,456 @@
|
||||
# Testowanie shopPRO
|
||||
|
||||
## Szybki start
|
||||
|
||||
### Pelny zestaw testow
|
||||
```bash
|
||||
composer test
|
||||
```
|
||||
|
||||
Alternatywnie (Windows):
|
||||
```bash
|
||||
./test.ps1
|
||||
./test.bat
|
||||
./test-simple.bat
|
||||
./test-debug.bat
|
||||
```
|
||||
|
||||
Alternatywnie (Git Bash):
|
||||
```bash
|
||||
./test.sh
|
||||
```
|
||||
|
||||
### Konkretny plik testowy
|
||||
```bash
|
||||
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||
./test.ps1 tests/Unit/admin/Controllers/ArticlesControllerTest.php
|
||||
```
|
||||
|
||||
### Konkretny test (`--filter`)
|
||||
```bash
|
||||
./test.ps1 --filter testGetQuantityReturnsCorrectValue
|
||||
```
|
||||
|
||||
## Aktualny stan suite
|
||||
|
||||
Ostatnio zweryfikowano: 2026-02-15
|
||||
|
||||
```text
|
||||
OK (351 tests, 1091 assertions)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji ShopClients (2026-02-15, ver. 0.274) - testy punktowe:
|
||||
```text
|
||||
OK (10 tests, 34 assertions)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji ShopCategory (2026-02-15, ver. 0.275) - testy punktowe:
|
||||
```text
|
||||
OK (16 tests, 72 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-15:
|
||||
- `tests/Unit/Domain/Client/ClientRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/ShopClientsControllerTest.php`
|
||||
- `tests/Unit/Domain/Category/CategoryRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/ShopCategoryControllerTest.php`
|
||||
|
||||
## Struktura testow
|
||||
|
||||
```text
|
||||
tests/
|
||||
|-- bootstrap.php
|
||||
|-- Unit/
|
||||
| |-- Domain/
|
||||
| | |-- Article/ArticleRepositoryTest.php
|
||||
| | |-- Attribute/AttributeRepositoryTest.php
|
||||
| | |-- Banner/BannerRepositoryTest.php
|
||||
| | |-- Cache/CacheRepositoryTest.php
|
||||
| | |-- Coupon/CouponRepositoryTest.php
|
||||
| | |-- Category/CategoryRepositoryTest.php
|
||||
| | |-- Dictionaries/DictionariesRepositoryTest.php
|
||||
| | |-- Integrations/IntegrationsRepositoryTest.php
|
||||
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
|
||||
| | |-- Producer/ProducerRepositoryTest.php
|
||||
| | |-- Product/ProductRepositoryTest.php
|
||||
| | |-- ProductSet/ProductSetRepositoryTest.php
|
||||
| | |-- Promotion/PromotionRepositoryTest.php
|
||||
| | |-- Settings/SettingsRepositoryTest.php
|
||||
| | |-- ShopStatus/ShopStatusRepositoryTest.php
|
||||
| | |-- Transport/TransportRepositoryTest.php
|
||||
| | `-- User/UserRepositoryTest.php
|
||||
| `-- admin/
|
||||
| `-- Controllers/
|
||||
| |-- ArticlesControllerTest.php
|
||||
| |-- DictionariesControllerTest.php
|
||||
| |-- IntegrationsControllerTest.php
|
||||
| |-- ProductArchiveControllerTest.php
|
||||
| |-- SettingsControllerTest.php
|
||||
| |-- ShopAttributeControllerTest.php
|
||||
| |-- ShopCategoryControllerTest.php
|
||||
| |-- ShopCouponControllerTest.php
|
||||
| |-- ShopPaymentMethodControllerTest.php
|
||||
| |-- ShopProducerControllerTest.php
|
||||
| |-- ShopProductControllerTest.php
|
||||
| |-- ShopProductSetsControllerTest.php
|
||||
| |-- ShopPromotionControllerTest.php
|
||||
| |-- ShopStatusesControllerTest.php
|
||||
| |-- ShopTransportControllerTest.php
|
||||
| `-- UsersControllerTest.php
|
||||
`-- Integration/
|
||||
```
|
||||
|
||||
## Tryby uruchamiania
|
||||
|
||||
### 1. TestDox (czytelna lista)
|
||||
```bash
|
||||
./test.bat
|
||||
```
|
||||
Uruchamia:
|
||||
```bash
|
||||
C:\xampp\php\php.exe phpunit.phar --testdox
|
||||
```
|
||||
|
||||
### 2. Standard (kropki)
|
||||
```bash
|
||||
./test-simple.bat
|
||||
```
|
||||
Uruchamia:
|
||||
```bash
|
||||
C:\xampp\php\php.exe phpunit.phar
|
||||
```
|
||||
|
||||
### 3. Debug (pelne logowanie)
|
||||
```bash
|
||||
./test-debug.bat
|
||||
```
|
||||
Uruchamia:
|
||||
```bash
|
||||
C:\xampp\php\php.exe phpunit.phar --debug
|
||||
```
|
||||
|
||||
### 4. PowerShell (najbardziej niezawodne)
|
||||
```bash
|
||||
./test.ps1
|
||||
```
|
||||
- najpierw probuje `php` z PATH
|
||||
- jesli brak, probuje m.in. `C:\xampp\php\php.exe`
|
||||
- zawsze dodaje `--do-not-cache-result`
|
||||
|
||||
## Interpretacja wynikow
|
||||
|
||||
```text
|
||||
. = test przeszedl
|
||||
E = error (blad wykonania)
|
||||
F = failure (niezgodna asercja)
|
||||
```
|
||||
|
||||
Przyklad sukcesu:
|
||||
```text
|
||||
................................................................. 65 / 82 ( 79%)
|
||||
................. 82 / 82 (100%)
|
||||
|
||||
OK (82 tests, 181 assertions)
|
||||
```
|
||||
|
||||
## Dodawanie nowych testow
|
||||
|
||||
1. Dodaj plik w odpowiednim module, np. `tests/Unit/Domain/<Module>/<Class>Test.php`.
|
||||
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
||||
3. Nazwy metod zaczynaj od `test`.
|
||||
4. Trzymaj sie wzorca AAA: Arrange, Act, Assert.
|
||||
|
||||
## Mockowanie (przyklad)
|
||||
|
||||
```php
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(42);
|
||||
|
||||
$repo = new ProductRepository($mockDb);
|
||||
$value = $repo->getQuantity(123);
|
||||
|
||||
$this->assertEquals(42, $value);
|
||||
```
|
||||
|
||||
## Przydatne informacje
|
||||
|
||||
- Konfiguracja PHPUnit: `phpunit.xml`
|
||||
- Bootstrap testow: `tests/bootstrap.php`
|
||||
- Dodatkowy opis: `tests/README.md`
|
||||
|
||||
## Aktualizacja suite
|
||||
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (119 tests, 256 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/User/UserRepositoryTest.php` (25 testow: CRUD, logon, 2FA verify/send, checkLogin, updateById)
|
||||
- `tests/Unit/admin/Controllers/UsersControllerTest.php` (12 testow: kontrakty + normalizeUser)
|
||||
|
||||
Aktualizacja po migracji widokow Users (2026-02-12):
|
||||
```text
|
||||
OK (120 tests, 262 assertions)
|
||||
```
|
||||
|
||||
## Aktualizacja suite (finalizacja Users)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (120 tests, 262 assertions)
|
||||
```
|
||||
|
||||
Aktualizacja po migracji Languages (2026-02-12):
|
||||
```text
|
||||
OK (130 tests, 301 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Languages/LanguagesRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/LanguagesControllerTest.php`
|
||||
|
||||
## Aktualizacja suite (release 0.254)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (130 tests, 301 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Languages/LanguagesRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/LanguagesControllerTest.php`
|
||||
|
||||
## Aktualizacja suite (release 0.255)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (130 tests, 303 assertions)
|
||||
```
|
||||
|
||||
## Aktualizacja suite (release 0.256)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (141 tests, 336 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Layouts/LayoutsRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/LayoutsControllerTest.php`
|
||||
|
||||
Zaktualizowane testy 2026-02-12:
|
||||
- `tests/Unit/Domain/Languages/LanguagesRepositoryTest.php` (defaultLanguageId)
|
||||
- `tests/Unit/admin/Controllers/ArticlesControllerTest.php` (konstruktor + LayoutsRepository)
|
||||
|
||||
## Aktualizacja suite (release 0.257)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (150 tests, 372 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Newsletter/NewsletterRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/NewsletterControllerTest.php`
|
||||
|
||||
## Aktualizacja suite (release 0.258)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (150 tests, 372 assertions)
|
||||
```
|
||||
|
||||
## Aktualizacja suite (release 0.259)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (158 tests, 397 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Scontainers/ScontainersRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/ScontainersControllerTest.php`
|
||||
|
||||
## Aktualizacja suite (release 0.260)
|
||||
Ostatnio zweryfikowano: 2026-02-12
|
||||
|
||||
```text
|
||||
OK (165 tests, 424 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-12:
|
||||
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (rozszerzenie o testy `restore`, `deletePermanently`, `listArchivedForAdmin`)
|
||||
- `tests/Unit/admin/Controllers/ArticlesArchiveControllerTest.php`
|
||||
|
||||
## Aktualizacja suite (release 0.261)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (176 tests, 439 assertions)
|
||||
```
|
||||
|
||||
Nowe testy/rozszerzenia 2026-02-13:
|
||||
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (nowe przypadki dla `pagesSummaryForArticles`, `updateImageAlt`, `markFileToDelete`)
|
||||
- `tests/Unit/admin/Controllers/ArticlesControllerTest.php` (nowe kontrakty dla akcji `imageAltChange`, `fileNameChange`, `imageDelete`, `fileDelete`)
|
||||
|
||||
## Aktualizacja suite (release 0.261)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (178 tests, 443 assertions)
|
||||
```
|
||||
|
||||
Nowe testy/rozszerzenia 2026-02-13:
|
||||
- `tests/Unit/Domain/Article/ArticleRepositoryTest.php` (nowe przypadki dla `saveFilesOrder`)
|
||||
|
||||
## Aktualizacja suite (Pages migration)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (186 tests, 478 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-13:
|
||||
- `tests/Unit/Domain/Pages/PagesRepositoryTest.php`
|
||||
- `tests/Unit/admin/Controllers/PagesControllerTest.php`
|
||||
|
||||
Zaktualizowane testy 2026-02-13:
|
||||
- `tests/Unit/admin/Controllers/ArticlesControllerTest.php` (konstruktor z `Domain\\Pages\\PagesRepository`)
|
||||
|
||||
## Aktualizacja suite (Integrations refactor, ver. 0.263)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (212 tests, 577 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-13:
|
||||
- `tests/Unit/Domain/Integrations/IntegrationsRepositoryTest.php` (16 testow: getSettings, getSetting, saveSetting, linkProduct, unlinkProduct, getProductSku, apiloGetAccessToken, invalid provider, settings table mapping)
|
||||
- `tests/Unit/admin/Controllers/IntegrationsControllerTest.php` (10 testow: kontrakty metod, return types, brak metod sellasist/baselinker)
|
||||
|
||||
Zaktualizowane pliki:
|
||||
- `tests/bootstrap.php` (dodany stub `S::remove_special_chars()`)
|
||||
|
||||
## Aktualizacja suite (ShopPromotion refactor, ver. 0.264)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (222 tests, 609 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-13:
|
||||
- `tests/Unit/Domain/Promotion/PromotionRepositoryTest.php` (6 testow: find default, save insert, delete, whitelist sortowania, drzewo kategorii)
|
||||
- `tests/Unit/admin/Controllers/ShopPromotionControllerTest.php` (4 testy: kontrakty metod i DI konstruktora)
|
||||
|
||||
## Aktualizacja suite (ShopPromotion fix + date_from, ver. 0.265)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (222 tests, 614 assertions)
|
||||
```
|
||||
|
||||
Zmiany testowe 2026-02-13:
|
||||
- rozszerzenie `tests/Unit/Domain/Promotion/PromotionRepositoryTest.php` o asercje `date_from`
|
||||
|
||||
## Aktualizacja suite (ShopCoupon refactor, ver. 0.266)
|
||||
Ostatnio zweryfikowano: 2026-02-13
|
||||
|
||||
```text
|
||||
OK (235 tests, 682 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-13:
|
||||
- `tests/Unit/Domain/Coupon/CouponRepositoryTest.php` (8 testow: find default/normalize, save insert/update, delete, whitelist sortowania, drzewo kategorii)
|
||||
- `tests/Unit/admin/Controllers/ShopCouponControllerTest.php` (5 testow: kontrakty metod, aliasy legacy, DI konstruktora)
|
||||
|
||||
Ponowna weryfikacja po poprawkach UI (drzewko + checkboxy): 2026-02-13
|
||||
- `OK (235 tests, 682 assertions)`
|
||||
|
||||
## Aktualizacja suite (ShopStatuses refactor, ver. 0.267)
|
||||
Ostatnio zweryfikowano: 2026-02-14
|
||||
|
||||
```text
|
||||
OK (254 tests, 736 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-14:
|
||||
- `tests/Unit/Domain/ShopStatus/ShopStatusRepositoryTest.php` (9 testow: find z ID=0, find null apilo, save update, save z ID=0, empty apilo sets null, reject negative ID, getApiloStatusId, getByIntegrationStatusId, allStatuses, whitelist sortowania)
|
||||
- `tests/Unit/admin/Controllers/ShopStatusesControllerTest.php` (5 testow: kontrakty metod, brak aliasow legacy, return types, DI konstruktora)
|
||||
|
||||
## Aktualizacja suite (ShopPaymentMethod refactor, ver. 0.268)
|
||||
Ostatnio zweryfikowano: 2026-02-14
|
||||
|
||||
```text
|
||||
OK (280 tests, 828 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-14:
|
||||
- `tests/Unit/Domain/PaymentMethod/PaymentMethodRepositoryTest.php` (14 testow: find invalid/null/normalize, save update/null/non-numeric apilo, listForAdmin whitelist, allActive, allForAdmin, findActiveById, isActive, getApiloPaymentTypeId, forTransport)
|
||||
- `tests/Unit/admin/Controllers/ShopPaymentMethodControllerTest.php` (5 testow: kontrakty metod, brak aliasow legacy, return types, DI konstruktora)
|
||||
|
||||
## Aktualizacja suite (ShopTransport refactor, ver. 0.269)
|
||||
Ostatnio zweryfikowano: 2026-02-14
|
||||
|
||||
```text
|
||||
OK (300 tests, 895 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-14:
|
||||
- `tests/Unit/Domain/Transport/TransportRepositoryTest.php` (14 testow: find invalid/null/normalize/nullables, save insert/update/failure/default reset/switch normalization, listForAdmin whitelist, allActive, getApiloCarrierAccountId, getTransportCost, allForAdmin)
|
||||
- `tests/Unit/admin/Controllers/ShopTransportControllerTest.php` (5 testow: kontrakty metod, brak aliasow legacy, return types, DI konstruktora z 2 repo)
|
||||
|
||||
## Aktualizacja suite (Apilo sync hardening, ver. 0.270)
|
||||
Ostatnio zweryfikowano: 2026-02-14
|
||||
|
||||
```text
|
||||
OK (300 tests, 895 assertions)
|
||||
```
|
||||
|
||||
Zmiany testowe 2026-02-14:
|
||||
- brak nowych testow; pelna regresja po zmianach sync Apilo (TPAY -> Apilo) przeszla bez bledow
|
||||
|
||||
## Aktualizacja suite (ShopAttribute refactor, ver. 0.271)
|
||||
Ostatnio zweryfikowano: 2026-02-14
|
||||
|
||||
```text
|
||||
OK (312 tests, 948 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-14:
|
||||
- `tests/Unit/Domain/Attribute/AttributeRepositoryTest.php` (5 testow: domyslne dane cechy, whitelist sortowania/paginacji, zapis wartosci i domyslnej, usuwanie pustych tlumaczen, jezyk domyslny)
|
||||
- `tests/Unit/admin/Controllers/ShopAttributeControllerTest.php` (7 testow: kontrakty metod, brak aliasow legacy, return types, DI konstruktora, walidacja `validateValuesRows`)
|
||||
|
||||
## Aktualizacja suite (ShopProductSets refactor, ver. 0.272)
|
||||
Ostatnio zweryfikowano: 2026-02-15
|
||||
|
||||
```text
|
||||
OK (324 tests, 1000 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-15:
|
||||
- `tests/Unit/Domain/ProductSet/ProductSetRepositoryTest.php` (7 testow: find default/normalize, save insert/update, delete invalid, whitelist sortowania/paginacji, allSets)
|
||||
- `tests/Unit/admin/Controllers/ShopProductSetsControllerTest.php` (5 testow: kontrakty metod, aliasy legacy, return types, DI konstruktora)
|
||||
|
||||
## Aktualizacja suite (ShopProducer refactor, ver. 0.273)
|
||||
Ostatnio zweryfikowano: 2026-02-15
|
||||
|
||||
```text
|
||||
OK (338 tests, 1063 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-15:
|
||||
- `tests/Unit/Domain/Producer/ProducerRepositoryTest.php` (9 testow: find default/normalize, save insert/update, delete invalid/success, whitelist sortowania/paginacji, allProducers, producerProducts)
|
||||
- `tests/Unit/admin/Controllers/ShopProducerControllerTest.php` (5 testow: kontrakty metod, aliasy legacy, return types, DI konstruktora)
|
||||
|
||||
## Aktualizacja suite (ShopProduct mass_edit, ver. 0.274)
|
||||
Ostatnio zweryfikowano: 2026-02-15
|
||||
|
||||
```text
|
||||
OK (351 tests, 1091 assertions)
|
||||
```
|
||||
|
||||
Nowe testy dodane 2026-02-15:
|
||||
- `tests/Unit/Domain/Product/ProductRepositoryTest.php` (rozszerzenie: `allProductsForMassEdit`, `getProductsByCategory`, `applyDiscountPercent`)
|
||||
- `tests/Unit/admin/Controllers/ShopProductControllerTest.php` (7 testow: kontrakty metod, return types, DI konstruktora)
|
||||
48
temp/update_build/tmp_0.275/libraries/grid/config.php
Normal file
48
temp/update_build/tmp_0.275/libraries/grid/config.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
error_reporting( E_ALL ^ E_NOTICE ^ E_STRICT ^ E_WARNING ^ E_DEPRECATED );
|
||||
define( 'gdir', "/libraries/grid" );
|
||||
require_once dirname( __FILE__ ) . '/grid-view.php';
|
||||
require_once dirname( __FILE__ ) . '/gdb.min.php';
|
||||
require_once dirname( __FILE__ ) . '/grid.php';
|
||||
require_once dirname( __FILE__ ) . '/grid-edit.php';
|
||||
require_once dirname( __FILE__ ) . '/grid-upload.php';
|
||||
|
||||
session_start();
|
||||
|
||||
/* połączenie z bazą ustawić wg własnych preferencji */
|
||||
require_once dirname( __FILE__ ) . '/../../config.php';
|
||||
require_once dirname( __FILE__ ) . '/../../autoload/class.S.php';
|
||||
|
||||
$legacyFactoryFiles = [
|
||||
'/../../autoload/admin/factory/class.Articles.php',
|
||||
'/../../autoload/admin/factory/class.Pages.php',
|
||||
'/../../autoload/admin/factory/class.ShopProduct.php',
|
||||
];
|
||||
|
||||
foreach ( $legacyFactoryFiles as $legacyFactoryFile )
|
||||
{
|
||||
$legacyFactoryPath = dirname( __FILE__ ) . $legacyFactoryFile;
|
||||
if ( file_exists( $legacyFactoryPath ) )
|
||||
require_once $legacyFactoryPath;
|
||||
}
|
||||
|
||||
require_once dirname( __FILE__ ) . '/../../autoload/shop/class.Product.php';
|
||||
require_once dirname( __FILE__ ) . '/../../libraries/medoo/medoo.php';
|
||||
|
||||
$gdb = array(
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
);
|
||||
|
||||
$mdb = new medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace Tests\Unit\admin\Controllers;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use admin\Controllers\ShopCategoryController;
|
||||
use Domain\Category\CategoryRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
|
||||
class ShopCategoryControllerTest extends TestCase
|
||||
{
|
||||
private $repository;
|
||||
private $languagesRepository;
|
||||
private $controller;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->createMock(CategoryRepository::class);
|
||||
$this->languagesRepository = $this->createMock(LanguagesRepository::class);
|
||||
$this->controller = new ShopCategoryController($this->repository, $this->languagesRepository);
|
||||
}
|
||||
|
||||
public function testConstructorAcceptsDependencies(): void
|
||||
{
|
||||
$controller = new ShopCategoryController($this->repository, $this->languagesRepository);
|
||||
$this->assertInstanceOf(ShopCategoryController::class, $controller);
|
||||
}
|
||||
|
||||
public function testHasExpectedActionMethods(): void
|
||||
{
|
||||
$this->assertTrue(method_exists($this->controller, 'view_list'));
|
||||
$this->assertTrue(method_exists($this->controller, 'list'));
|
||||
$this->assertTrue(method_exists($this->controller, 'category_edit'));
|
||||
$this->assertTrue(method_exists($this->controller, 'edit'));
|
||||
$this->assertTrue(method_exists($this->controller, 'save'));
|
||||
$this->assertTrue(method_exists($this->controller, 'category_delete'));
|
||||
$this->assertTrue(method_exists($this->controller, 'delete'));
|
||||
$this->assertTrue(method_exists($this->controller, 'category_products'));
|
||||
$this->assertTrue(method_exists($this->controller, 'products'));
|
||||
$this->assertTrue(method_exists($this->controller, 'category_url_browser'));
|
||||
$this->assertTrue(method_exists($this->controller, 'save_categories_order'));
|
||||
$this->assertTrue(method_exists($this->controller, 'save_products_order'));
|
||||
$this->assertTrue(method_exists($this->controller, 'cookie_categories'));
|
||||
}
|
||||
|
||||
public function testViewActionsReturnString(): void
|
||||
{
|
||||
$reflection = new \ReflectionClass($this->controller);
|
||||
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('view_list')->getReturnType());
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('list')->getReturnType());
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('category_edit')->getReturnType());
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('edit')->getReturnType());
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('category_products')->getReturnType());
|
||||
$this->assertEquals('string', (string)$reflection->getMethod('products')->getReturnType());
|
||||
}
|
||||
|
||||
public function testMutationActionsReturnVoid(): void
|
||||
{
|
||||
$reflection = new \ReflectionClass($this->controller);
|
||||
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('save')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('category_delete')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('delete')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('category_url_browser')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('save_categories_order')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('save_products_order')->getReturnType());
|
||||
$this->assertEquals('void', (string)$reflection->getMethod('cookie_categories')->getReturnType());
|
||||
}
|
||||
|
||||
public function testConstructorRequiresCategoryAndLanguagesRepositories(): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(ShopCategoryController::class);
|
||||
$constructor = $reflection->getConstructor();
|
||||
$params = $constructor->getParameters();
|
||||
|
||||
$this->assertCount(2, $params);
|
||||
$this->assertEquals('Domain\\Category\\CategoryRepository', $params[0]->getType()->getName());
|
||||
$this->assertEquals('Domain\\Languages\\LanguagesRepository', $params[1]->getType()->getName());
|
||||
}
|
||||
}
|
||||
BIN
temp/update_build/update_0.275.zip
Normal file
BIN
temp/update_build/update_0.275.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user