Add view classes for articles, banners, languages, menu, newsletter, containers, shop categories, clients, payment methods, products, and search
- Created Articles.php for rendering article views including full articles, miniature lists, and news sections. - Added Banners.php for handling banner displays. - Introduced Languages.php for rendering language options. - Implemented Menu.php for dynamic menu rendering. - Developed Newsletter.php for newsletter view rendering. - Created Scontainers.php for rendering specific containers. - Added ShopCategory.php for category descriptions and product listings. - Introduced ShopClient.php for managing client-related views such as address editing and order history. - Implemented ShopPaymentMethod.php for displaying payment methods in the basket. - Created ShopProduct.php for generating product URLs. - Added ShopSearch.php for rendering a simple search form. - Added .htaccess file to enhance security by restricting access to sensitive files and directories.
This commit is contained in:
1170
autoload/Domain/Article/ArticleRepository.php
Normal file
1170
autoload/Domain/Article/ArticleRepository.php
Normal file
File diff suppressed because it is too large
Load Diff
1011
autoload/Domain/Attribute/AttributeRepository.php
Normal file
1011
autoload/Domain/Attribute/AttributeRepository.php
Normal file
File diff suppressed because it is too large
Load Diff
399
autoload/Domain/Banner/BannerRepository.php
Normal file
399
autoload/Domain/Banner/BannerRepository.php
Normal file
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
namespace Domain\Banner;
|
||||
|
||||
/**
|
||||
* Repository odpowiedzialny za dostęp do danych banerów
|
||||
*/
|
||||
class BannerRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera baner po ID wraz z tłumaczeniami
|
||||
*
|
||||
* @param int $bannerId ID banera
|
||||
* @return array|null Dane banera lub null
|
||||
*/
|
||||
public function find(int $bannerId): ?array
|
||||
{
|
||||
$banner = $this->db->get('pp_banners', '*', ['id' => $bannerId]);
|
||||
|
||||
if (!$banner) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$results = $this->db->select('pp_banners_langs', '*', [
|
||||
'id_banner' => $bannerId,
|
||||
'ORDER' => ['id' => 'ASC'],
|
||||
]);
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$banner['languages'][$row['id_lang']] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $banner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuwa baner
|
||||
*
|
||||
* @param int $bannerId ID banera
|
||||
* @return bool Czy usunięto
|
||||
*/
|
||||
public function delete(int $bannerId): bool
|
||||
{
|
||||
$result = $this->db->delete('pp_banners', ['id' => $bannerId]);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje baner (insert lub update)
|
||||
*
|
||||
* @param array $data Dane banera (obsługuje format z FormRequestHandler lub stary format)
|
||||
* @return int|false ID banera lub false
|
||||
*/
|
||||
public function save(array $data)
|
||||
{
|
||||
$bannerId = $data['id'] ?? null;
|
||||
|
||||
// Obsługa obu formatów: nowy (int) i stary ('on'/string)
|
||||
$status = $data['status'] ?? 0;
|
||||
if ($status === 'on') {
|
||||
$status = 1;
|
||||
}
|
||||
|
||||
$homePage = $data['home_page'] ?? 0;
|
||||
if ($homePage === 'on') {
|
||||
$homePage = 1;
|
||||
}
|
||||
|
||||
$bannerData = [
|
||||
'name' => $data['name'],
|
||||
'status' => (int)$status,
|
||||
'date_start' => !empty($data['date_start']) ? $data['date_start'] : null,
|
||||
'date_end' => !empty($data['date_end']) ? $data['date_end'] : null,
|
||||
'home_page' => (int)$homePage,
|
||||
];
|
||||
|
||||
if (!$bannerId) {
|
||||
$this->db->insert('pp_banners', $bannerData);
|
||||
$bannerId = $this->db->id();
|
||||
if (!$bannerId) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_banners', $bannerData, ['id' => (int)$bannerId]);
|
||||
}
|
||||
|
||||
// Obsługa danych językowych - nowy format (translations) lub stary (src/url/html/text)
|
||||
if (isset($data['translations']) && is_array($data['translations'])) {
|
||||
// Nowy format z FormRequestHandler
|
||||
$this->saveTranslationsFromArray($bannerId, $data['translations']);
|
||||
} elseif (isset($data['src']) && is_array($data['src'])) {
|
||||
// Stary format (backward compatibility)
|
||||
$this->saveTranslations($bannerId, $data['src'], $data['url'], $data['html'], $data['text']);
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
return (int)$bannerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca liste banerow do panelu admin z filtrowaniem, sortowaniem i paginacja.
|
||||
*
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$sortColumn = trim($sortColumn);
|
||||
$sortDir = strtoupper(trim($sortDir));
|
||||
|
||||
$allowedSortColumns = [
|
||||
'name' => 'b.name',
|
||||
'status' => 'b.status',
|
||||
'home_page' => 'b.home_page',
|
||||
'date_start' => 'b.date_start',
|
||||
'date_end' => 'b.date_end',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'b.name';
|
||||
$sortDir = $sortDir === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1=1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
|
||||
if ($name !== '') {
|
||||
$where[] = 'b.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
if (($filters['status'] ?? '') !== '' && ($filters['status'] === '0' || $filters['status'] === '1')) {
|
||||
$where[] = 'b.status = :status';
|
||||
$params[':status'] = (int)$filters['status'];
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_banners AS b
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
b.id,
|
||||
b.name,
|
||||
b.status,
|
||||
b.home_page,
|
||||
b.date_start,
|
||||
b.date_end
|
||||
FROM pp_banners AS b
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, b.id {$sortDir}
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
$items = is_array($items) ? $items : [];
|
||||
|
||||
if (!empty($items)) {
|
||||
$bannerIds = array_map('intval', array_column($items, 'id'));
|
||||
$thumbByBannerId = $this->fetchThumbnailsByBannerIds($bannerIds);
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['thumbnail_src'] = $thumbByBannerId[(int)($item['id'] ?? 0)] ?? '';
|
||||
}
|
||||
unset($item);
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera pierwsza dostepna sciezke obrazka (src) dla kazdego banera.
|
||||
*
|
||||
* @param array<int, int> $bannerIds
|
||||
* @return array<int, string> [id_banner => src]
|
||||
*/
|
||||
private function fetchThumbnailsByBannerIds(array $bannerIds): array
|
||||
{
|
||||
$bannerIds = array_values(array_unique(array_filter($bannerIds, static function ($id): bool {
|
||||
return (int)$id > 0;
|
||||
})));
|
||||
|
||||
if (empty($bannerIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$in = [];
|
||||
$params = [];
|
||||
foreach ($bannerIds as $index => $bannerId) {
|
||||
$placeholder = ':id' . $index;
|
||||
$in[] = $placeholder;
|
||||
$params[$placeholder] = (int)$bannerId;
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT id_banner, src
|
||||
FROM pp_banners_langs
|
||||
WHERE id_banner IN (' . implode(', ', $in) . ')
|
||||
AND src IS NOT NULL
|
||||
AND src <> \'\'
|
||||
ORDER BY id_lang ASC, id ASC
|
||||
';
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$thumbByBannerId = [];
|
||||
foreach ($rows as $row) {
|
||||
$bannerId = (int)($row['id_banner'] ?? 0);
|
||||
if ($bannerId <= 0 || isset($thumbByBannerId[$bannerId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$src = trim((string)($row['src'] ?? ''));
|
||||
if ($src !== '') {
|
||||
$thumbByBannerId[$bannerId] = $src;
|
||||
}
|
||||
}
|
||||
|
||||
return $thumbByBannerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje tłumaczenia banera (stary format - zachowano dla kompatybilności)
|
||||
*/
|
||||
private function saveTranslations(int $bannerId, array $src, array $url, array $html, array $text): void
|
||||
{
|
||||
foreach ($src as $langId => $val) {
|
||||
$this->upsertTranslation($bannerId, $langId, [
|
||||
'src' => $src[$langId] ?? '',
|
||||
'url' => $url[$langId] ?? '',
|
||||
'html' => $html[$langId] ?? '',
|
||||
'text' => $text[$langId] ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje tłumaczenia banera z nowego formatu (z FormRequestHandler)
|
||||
* Format: [lang_id => [field => value]]
|
||||
*/
|
||||
private function saveTranslationsFromArray(int $bannerId, array $translations): void
|
||||
{
|
||||
foreach ($translations as $langId => $fields) {
|
||||
$this->upsertTranslation($bannerId, $langId, [
|
||||
'src' => $fields['src'] ?? '',
|
||||
'url' => $fields['url'] ?? '',
|
||||
'html' => $fields['html'] ?? '',
|
||||
'text' => $fields['text'] ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert tlumaczenia banera.
|
||||
* Aktualizuje wszystkie rekordy dla pary id_banner + id_lang,
|
||||
* co usuwa problem z historycznymi duplikatami.
|
||||
*/
|
||||
private function upsertTranslation(int $bannerId, $langId, array $fields): void
|
||||
{
|
||||
$where = ['AND' => ['id_banner' => $bannerId, 'id_lang' => $langId]];
|
||||
$translationData = [
|
||||
'id_banner' => $bannerId,
|
||||
'id_lang' => $langId,
|
||||
'src' => $fields['src'] ?? '',
|
||||
'url' => $fields['url'] ?? '',
|
||||
'html' => $fields['html'] ?? '',
|
||||
'text' => $fields['text'] ?? '',
|
||||
];
|
||||
|
||||
$hasExisting = (int)$this->db->count('pp_banners_langs', $where) > 0;
|
||||
|
||||
if ($hasExisting) {
|
||||
$this->db->update('pp_banners_langs', $translationData, $where);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_banners_langs', $translationData);
|
||||
}
|
||||
|
||||
// ─── Frontend methods ───────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Pobiera aktywne banery (home_page = 0) z filtrowaniem dat, z Redis cache.
|
||||
* Zwraca dane w formacie zgodnym z szablonami: $banner['languages'] = płaski wiersz.
|
||||
*/
|
||||
public function banners(string $langId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "BannerRepository::banners:{$langId}";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$stmt = $this->db->query(
|
||||
"SELECT id, name FROM pp_banners "
|
||||
. "WHERE status = 1 "
|
||||
. "AND (date_start <= :today1 OR date_start IS NULL) "
|
||||
. "AND (date_end >= :today2 OR date_end IS NULL) "
|
||||
. "AND home_page = 0",
|
||||
[':today1' => $today, ':today2' => $today]
|
||||
);
|
||||
$results = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
$banners = null;
|
||||
if (is_array($results) && !empty($results)) {
|
||||
foreach ($results as $row) {
|
||||
$row['languages'] = $this->db->get('pp_banners_langs', '*', [
|
||||
'AND' => ['id_banner' => (int)$row['id'], 'id_lang' => $langId]
|
||||
]);
|
||||
$banners[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $banners);
|
||||
|
||||
return $banners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera glowny baner (home_page = 1) z filtrowaniem dat, z Redis cache.
|
||||
* Zwraca dane w formacie zgodnym z szablonami: $banner['languages'] = plaski wiersz.
|
||||
*/
|
||||
public function mainBanner(string $langId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "BannerRepository::mainBanner:{$langId}";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$today = date('Y-m-d');
|
||||
$stmt = $this->db->query(
|
||||
"SELECT * FROM pp_banners "
|
||||
. "WHERE status = 1 "
|
||||
. "AND (date_start <= :today1 OR date_start IS NULL) "
|
||||
. "AND (date_end >= :today2 OR date_end IS NULL) "
|
||||
. "AND home_page = 1 "
|
||||
. "ORDER BY date_end ASC "
|
||||
. "LIMIT 1",
|
||||
[':today1' => $today, ':today2' => $today]
|
||||
);
|
||||
$results = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
$banner = null;
|
||||
if (is_array($results) && !empty($results)) {
|
||||
$banner = $results[0];
|
||||
$banner['languages'] = $this->db->get('pp_banners_langs', '*', [
|
||||
'AND' => ['id_banner' => (int)$banner['id'], 'id_lang' => $langId]
|
||||
]);
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $banner);
|
||||
|
||||
return $banner;
|
||||
}
|
||||
}
|
||||
238
autoload/Domain/Basket/BasketCalculator.php
Normal file
238
autoload/Domain/Basket/BasketCalculator.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
namespace Domain\Basket;
|
||||
|
||||
class BasketCalculator
|
||||
{
|
||||
public static function summaryWp($basket)
|
||||
{
|
||||
$wp = 0;
|
||||
if (is_array($basket)) {
|
||||
foreach ($basket as $product) {
|
||||
$wp += $product['wp'] * $product['quantity'];
|
||||
}
|
||||
}
|
||||
return $wp;
|
||||
}
|
||||
|
||||
public static function countProductsText($count)
|
||||
{
|
||||
$count = (int)$count;
|
||||
if ($count === 1) {
|
||||
return $count . ' produkt';
|
||||
}
|
||||
if ($count >= 2 && $count <= 4) {
|
||||
return $count . ' produkty';
|
||||
}
|
||||
return $count . ' produktów';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $langId Language ID (falls back to global $lang_id if null)
|
||||
* @param \Domain\Product\ProductRepository|null $productRepo (falls back to $GLOBALS['mdb'] if null)
|
||||
*/
|
||||
public static function summaryPrice($basket, $coupon = null, $langId = null, $productRepo = null)
|
||||
{
|
||||
if ($langId === null) {
|
||||
global $lang_id;
|
||||
$langId = $lang_id;
|
||||
}
|
||||
if ($productRepo === null) {
|
||||
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
|
||||
}
|
||||
|
||||
$summary = 0;
|
||||
|
||||
if (is_array($basket)) {
|
||||
foreach ($basket as $position) {
|
||||
$product = $productRepo->findCached((int)$position['product-id'], $langId);
|
||||
|
||||
$product_price_tmp = self::calculateBasketProductPrice(
|
||||
(float)($product['price_brutto_promo'] ?? 0),
|
||||
(float)($product['price_brutto'] ?? 0),
|
||||
$coupon,
|
||||
$position,
|
||||
$productRepo
|
||||
);
|
||||
$summary += $product_price_tmp['price_new'] * $position['quantity'];
|
||||
}
|
||||
}
|
||||
|
||||
return \Shared\Helpers\Helpers::normalize_decimal($summary);
|
||||
}
|
||||
|
||||
public static function countProducts($basket)
|
||||
{
|
||||
$count = 0;
|
||||
if (is_array($basket)) {
|
||||
foreach ($basket as $product) {
|
||||
$count += $product['quantity'];
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
public static function validateBasket($basket)
|
||||
{
|
||||
if ( !is_array( $basket ) )
|
||||
return array();
|
||||
|
||||
return $basket;
|
||||
}
|
||||
|
||||
public static function checkProductQuantityInStock($basket, bool $message = false)
|
||||
{
|
||||
if ( !is_array( $basket ) || empty( $basket ) )
|
||||
return false;
|
||||
|
||||
$result = false;
|
||||
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$permutation = null;
|
||||
|
||||
if ( isset( $val['parent_id'] ) and (int)$val['parent_id'] and isset( $val['product-id'] ) )
|
||||
$permutation = $productRepo->getProductPermutationHash( (int)$val['product-id'] );
|
||||
|
||||
if ( !$permutation and isset( $val['attributes'] ) and is_array( $val['attributes'] ) and count( $val['attributes'] ) )
|
||||
$permutation = implode( '|', $val['attributes'] );
|
||||
|
||||
$quantity_options = $productRepo->getProductPermutationQuantityOptions(
|
||||
$val['parent_id'] ? $val['parent_id'] : $val['product-id'],
|
||||
$permutation
|
||||
);
|
||||
|
||||
if (
|
||||
(int)$basket[ $key ][ 'quantity' ] < 1
|
||||
and ( (int)$quantity_options['quantity'] > 0 or (int)$quantity_options['stock_0_buy'] === 1 )
|
||||
)
|
||||
{
|
||||
$basket[ $key ][ 'quantity' ] = 1;
|
||||
$result = true;
|
||||
}
|
||||
|
||||
if ( ( $val[ 'quantity' ] > $quantity_options['quantity'] ) and !$quantity_options['stock_0_buy'] )
|
||||
{
|
||||
$basket[ $key ][ 'quantity' ] = $quantity_options['quantity'];
|
||||
if ( $message )
|
||||
\Shared\Helpers\Helpers::error( 'Ilość jednego lub więcej produktów została zmniejszona z powodu niestarczających stanów magazynowych. Sprawdź proszę koszyk.' );
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
\Shared\Helpers\Helpers::set_session( 'basket', $basket );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate product price in basket (with coupon + promotion discounts).
|
||||
* Migrated from \shop\Product::calculate_basket_product_price()
|
||||
*/
|
||||
/**
|
||||
* @param \Domain\Product\ProductRepository|null $productRepo (falls back to $GLOBALS['mdb'] if null)
|
||||
*/
|
||||
public static function calculateBasketProductPrice( float $price_brutto_promo, float $price_brutto, $coupon, $basket_position, $productRepo = null )
|
||||
{
|
||||
if ($productRepo === null) {
|
||||
$productRepo = new \Domain\Product\ProductRepository($GLOBALS['mdb']);
|
||||
}
|
||||
|
||||
// Produkty przecenione
|
||||
if ( $price_brutto_promo )
|
||||
{
|
||||
$price['price'] = $price_brutto;
|
||||
$price['price_new'] = $price_brutto_promo;
|
||||
|
||||
$coupon_type = is_array($coupon) ? ($coupon['type'] ?? null) : (is_object($coupon) ? $coupon->type : null);
|
||||
$coupon_include_discounted = is_array($coupon) ? ($coupon['include_discounted_product'] ?? null) : (is_object($coupon) ? $coupon->include_discounted_product : null);
|
||||
$coupon_categories = is_array($coupon) ? ($coupon['categories'] ?? null) : (is_object($coupon) ? $coupon->categories : null);
|
||||
$coupon_amount = is_array($coupon) ? ($coupon['amount'] ?? 0) : (is_object($coupon) ? $coupon->amount : 0);
|
||||
|
||||
if ( $coupon_type && $coupon_include_discounted )
|
||||
{
|
||||
if ( $coupon_categories != null )
|
||||
{
|
||||
$cats = is_string($coupon_categories) ? json_decode($coupon_categories) : $coupon_categories;
|
||||
$product_categories = $productRepo->productCategories( (int)$basket_position['parent_id'] ? (int)$basket_position['parent_id'] : (int)$basket_position['product-id'] );
|
||||
if ( is_array( $cats ) ) foreach ( $cats as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp, $product_categories ) )
|
||||
{
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
|
||||
|
||||
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
if ( $basket_position['discount_type'] == 1 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price_new'] - $price['price_new'] * $basket_position['discount_amount'] / 100 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $basket_position['discount_amount'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
if ( $basket_position['discount_type'] == 1 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price_new'] - $price['price_new'] * $basket_position['discount_amount'] / 100 );
|
||||
}
|
||||
}
|
||||
}
|
||||
// Produkt nieprzeceniony
|
||||
else
|
||||
{
|
||||
$price['price'] = $price_brutto;
|
||||
$price['price_new'] = $price_brutto;
|
||||
|
||||
$coupon_type = is_array($coupon) ? ($coupon['type'] ?? null) : (is_object($coupon) ? $coupon->type : null);
|
||||
$coupon_categories = is_array($coupon) ? ($coupon['categories'] ?? null) : (is_object($coupon) ? $coupon->categories : null);
|
||||
$coupon_amount = is_array($coupon) ? ($coupon['amount'] ?? 0) : (is_object($coupon) ? $coupon->amount : 0);
|
||||
|
||||
if ( $coupon_type )
|
||||
{
|
||||
if ( $coupon_categories != null )
|
||||
{
|
||||
$cats = is_string($coupon_categories) ? json_decode($coupon_categories) : $coupon_categories;
|
||||
$product_categories = $productRepo->productCategories( $basket_position['parent_id'] ? $basket_position['parent_id'] : $basket_position['product-id'] );
|
||||
if ( is_array( $cats ) ) foreach ( $cats as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp, $product_categories ) )
|
||||
{
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $price['price_new'] - $price['price_new'] * $coupon_amount / 100 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $coupon_amount / 100 );
|
||||
|
||||
if ( $basket_position['discount_amount'] && $basket_position['discount_include_coupon'] && $basket_position['include_product_promo'] )
|
||||
{
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
if ( $basket_position['discount_type'] == 1 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $basket_position['discount_amount'] / 100 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $basket_position['discount_amount'] )
|
||||
{
|
||||
if ( $basket_position['discount_type'] == 3 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal( $basket_position['discount_amount'] );
|
||||
if ( $basket_position['discount_type'] == 1 )
|
||||
$price['price_new'] = \Shared\Helpers\Helpers::normalize_decimal($price['price'] - $price['price'] * $basket_position['discount_amount'] / 100 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $price;
|
||||
}
|
||||
}
|
||||
53
autoload/Domain/Cache/CacheRepository.php
Normal file
53
autoload/Domain/Cache/CacheRepository.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace Domain\Cache;
|
||||
|
||||
/**
|
||||
* Repozytorium zarządzania cache (katalogi + Redis)
|
||||
*
|
||||
* Wyodrębniona logika czyszczenia cache z wstrzykiwanymi zależnościami.
|
||||
* Obecnie nie używane przez SettingsController (cache czyszczony bezpośrednio).
|
||||
* Przygotowane na przyszłe użycie w innych kontrolerach.
|
||||
*/
|
||||
class CacheRepository
|
||||
{
|
||||
private $redisConnection;
|
||||
private $basePath;
|
||||
|
||||
/**
|
||||
* @param \Shared\Cache\RedisConnection $redisConnection Połączenie z Redis (nullable)
|
||||
* @param string $basePath Ścieżka bazowa do katalogów cache
|
||||
*/
|
||||
public function __construct(
|
||||
?\Shared\Cache\RedisConnection $redisConnection = null,
|
||||
string $basePath = '../'
|
||||
) {
|
||||
$this->redisConnection = $redisConnection;
|
||||
$this->basePath = $basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyszczenie całego cache (katalogi + Redis)
|
||||
*
|
||||
* @return array ['success' => bool, 'message' => string]
|
||||
*/
|
||||
public function clearCache(): array
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_dir( $this->basePath . 'temp/' );
|
||||
\Shared\Helpers\Helpers::delete_dir( $this->basePath . 'thumbs/' );
|
||||
|
||||
$redisCleared = false;
|
||||
if ( $this->redisConnection ) {
|
||||
$redis = $this->redisConnection->getConnection();
|
||||
if ( $redis ) {
|
||||
$redis->flushAll();
|
||||
$redisCleared = true;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Cache został wyczyszczony.',
|
||||
'redisCleared' => $redisCleared
|
||||
];
|
||||
}
|
||||
}
|
||||
772
autoload/Domain/Category/CategoryRepository.php
Normal file
772
autoload/Domain/Category/CategoryRepository.php
Normal file
@@ -0,0 +1,772 @@
|
||||
<?php
|
||||
namespace Domain\Category;
|
||||
|
||||
class CategoryRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
private const SORT_TYPES = [
|
||||
0 => 'data dodania - najstarsze na początku',
|
||||
1 => 'data dodania - najnowsze na początku',
|
||||
2 => 'data modyfikacji - rosnąco',
|
||||
3 => 'data mofyfikacji - malejąco',
|
||||
4 => 'ręczne',
|
||||
5 => 'alfabetycznie - A - Z',
|
||||
6 => 'alfabetycznie - Z - A',
|
||||
];
|
||||
|
||||
private const SORT_ORDER_SQL = [
|
||||
0 => 'q1.date_add ASC',
|
||||
1 => 'q1.date_add DESC',
|
||||
2 => 'q1.date_modify ASC',
|
||||
3 => 'q1.date_modify DESC',
|
||||
4 => 'q1.o ASC',
|
||||
5 => 'q1.name ASC',
|
||||
6 => 'q1.name DESC',
|
||||
];
|
||||
|
||||
private const PRODUCTS_PER_PAGE = 12;
|
||||
|
||||
private const LANGUAGE_FALLBACK_NAME_SQL = '(CASE '
|
||||
. 'WHEN copy_from IS NULL THEN name '
|
||||
. 'WHEN copy_from IS NOT NULL THEN ('
|
||||
. 'SELECT name FROM pp_shop_products_langs '
|
||||
. 'WHERE lang_id = pspl.copy_from AND product_id = psp.id'
|
||||
. ') '
|
||||
. 'END) AS name';
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function sortTypes(): array
|
||||
{
|
||||
return self::SORT_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function subcategories($parentId = null): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_categories', ['id'], [
|
||||
'parent_id' => $this->toNullableInt($parentId),
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach ($rows as $row) {
|
||||
$categoryId = (int)($row['id'] ?? 0);
|
||||
if ($categoryId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$details = $this->categoryDetails($categoryId);
|
||||
if (!empty($details)) {
|
||||
$categories[] = $details;
|
||||
}
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function categoryDetails($categoryId = ''): array
|
||||
{
|
||||
$id = (int)$categoryId;
|
||||
if ($id <= 0) {
|
||||
return $this->defaultCategory();
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $id]);
|
||||
if (!is_array($category)) {
|
||||
return $this->defaultCategory();
|
||||
}
|
||||
|
||||
$category['id'] = (int)($category['id'] ?? 0);
|
||||
$category['status'] = $this->toSwitchValue($category['status'] ?? 0);
|
||||
$category['view_subcategories'] = $this->toSwitchValue($category['view_subcategories'] ?? 0);
|
||||
$category['sort_type'] = (int)($category['sort_type'] ?? 0);
|
||||
$category['parent_id'] = $this->toNullableInt($category['parent_id'] ?? null);
|
||||
|
||||
$translations = $this->db->select('pp_shop_categories_langs', '*', ['category_id' => $id]);
|
||||
$category['languages'] = [];
|
||||
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $translation) {
|
||||
$langId = (string)($translation['lang_id'] ?? '');
|
||||
if ($langId === '') {
|
||||
continue;
|
||||
}
|
||||
$category['languages'][$langId] = $translation;
|
||||
}
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoryProducts(int $categoryId): array
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT '
|
||||
. 'pspc.product_id, pspc.o, psp.status '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'pspc.category_id = :category_id '
|
||||
. 'ORDER BY '
|
||||
. 'pspc.o ASC',
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
]
|
||||
);
|
||||
|
||||
if (!$rows) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$products = [];
|
||||
foreach ($rows->fetchAll() as $row) {
|
||||
$productId = (int)($row['product_id'] ?? 0);
|
||||
if ($productId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$products[] = [
|
||||
'product_id' => $productId,
|
||||
'o' => (int)($row['o'] ?? 0),
|
||||
'status' => $this->toSwitchValue($row['status'] ?? 0),
|
||||
'name' => $this->productName($productId),
|
||||
];
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
public function categoryDelete($categoryId): bool
|
||||
{
|
||||
$id = (int)$categoryId;
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_shop_categories', ['parent_id' => $id]) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$deleted = (bool)$this->db->delete('pp_shop_categories', ['id' => $id]);
|
||||
if ($deleted) {
|
||||
$this->refreshCategoryArtifacts();
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
public function saveCategoriesOrder($categories): bool
|
||||
{
|
||||
if (!is_array($categories)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_categories', ['o' => 0]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($categories as $item) {
|
||||
$itemId = (int)($item['item_id'] ?? 0);
|
||||
if ($itemId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$parentId = $this->toNullableInt($item['parent_id'] ?? null);
|
||||
$this->db->update('pp_shop_categories', [
|
||||
'o' => $position,
|
||||
'parent_id' => $parentId,
|
||||
], [
|
||||
'id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->refreshCategoryArtifacts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveProductOrder($categoryId, $products): bool
|
||||
{
|
||||
$id = (int)$categoryId;
|
||||
if ($id <= 0 || !is_array($products)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_products_categories', ['o' => 0], ['category_id' => $id]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($products as $item) {
|
||||
$productId = (int)($item['item_id'] ?? 0);
|
||||
if ($productId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$this->db->update('pp_shop_products_categories', ['o' => $position], [
|
||||
'AND' => [
|
||||
'category_id' => $id,
|
||||
'product_id' => $productId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$categoryId = (int)($data['id'] ?? 0);
|
||||
$parentId = $this->toNullableInt($data['parent_id'] ?? null);
|
||||
|
||||
$row = [
|
||||
'status' => $this->toSwitchValue($data['status'] ?? 0),
|
||||
'parent_id' => $parentId,
|
||||
'sort_type' => (int)($data['sort_type'] ?? 0),
|
||||
'view_subcategories' => $this->toSwitchValue($data['view_subcategories'] ?? 0),
|
||||
];
|
||||
|
||||
if ($categoryId <= 0) {
|
||||
$row['o'] = $this->maxOrder() + 1;
|
||||
$this->db->insert('pp_shop_categories', $row);
|
||||
$categoryId = (int)$this->db->id();
|
||||
|
||||
if ($categoryId <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_shop_categories', $row, ['id' => $categoryId]);
|
||||
}
|
||||
|
||||
$title = is_array($data['title'] ?? null) ? $data['title'] : [];
|
||||
$text = is_array($data['text'] ?? null) ? $data['text'] : [];
|
||||
$textHidden = is_array($data['text_hidden'] ?? null) ? $data['text_hidden'] : [];
|
||||
$seoLink = is_array($data['seo_link'] ?? null) ? $data['seo_link'] : [];
|
||||
$metaTitle = is_array($data['meta_title'] ?? null) ? $data['meta_title'] : [];
|
||||
$metaDescription = is_array($data['meta_description'] ?? null) ? $data['meta_description'] : [];
|
||||
$metaKeywords = is_array($data['meta_keywords'] ?? null) ? $data['meta_keywords'] : [];
|
||||
$noindex = is_array($data['noindex'] ?? null) ? $data['noindex'] : [];
|
||||
$categoryTitle = is_array($data['category_title'] ?? null) ? $data['category_title'] : [];
|
||||
$additionalText = is_array($data['additional_text'] ?? null) ? $data['additional_text'] : [];
|
||||
|
||||
foreach ($title as $langId => $langTitle) {
|
||||
$translationData = [
|
||||
'lang_id' => (string)$langId,
|
||||
'title' => $this->toNullableString($langTitle),
|
||||
'text' => $this->toNullableString($text[$langId] ?? null),
|
||||
'text_hidden' => $this->toNullableString($textHidden[$langId] ?? null),
|
||||
'meta_description' => $this->toNullableString($metaDescription[$langId] ?? null),
|
||||
'meta_keywords' => $this->toNullableString($metaKeywords[$langId] ?? null),
|
||||
'meta_title' => $this->toNullableString($metaTitle[$langId] ?? null),
|
||||
'seo_link' => $this->normalizeSeoLink($seoLink[$langId] ?? null),
|
||||
'noindex' => (int)($noindex[$langId] ?? 0),
|
||||
'category_title' => $this->toNullableString($categoryTitle[$langId] ?? null),
|
||||
'additional_text' => $this->toNullableString($additionalText[$langId] ?? null),
|
||||
];
|
||||
|
||||
$translationId = $this->db->get('pp_shop_categories_langs', 'id', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'lang_id' => (string)$langId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($translationId) {
|
||||
$this->db->update('pp_shop_categories_langs', $translationData, ['id' => $translationId]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_shop_categories_langs', array_merge($translationData, [
|
||||
'category_id' => $categoryId,
|
||||
]));
|
||||
}
|
||||
|
||||
$this->refreshCategoryArtifacts();
|
||||
|
||||
return $categoryId;
|
||||
}
|
||||
|
||||
public function categoryTitle(int $categoryId): string
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$title = $this->db->select('pp_shop_categories_langs', [
|
||||
'[><]pp_langs' => ['lang_id' => 'id'],
|
||||
], 'title', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'title[!]' => '',
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
'LIMIT' => 1,
|
||||
]);
|
||||
|
||||
if (!is_array($title) || !isset($title[0])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string)$title[0];
|
||||
}
|
||||
|
||||
// ===== Frontend methods =====
|
||||
|
||||
public function getCategorySort(int $categoryId): int
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "get_category_sort:$categoryId";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (int)unserialize($objectData);
|
||||
}
|
||||
|
||||
$sortType = (int)$this->db->get('pp_shop_categories', 'sort_type', ['id' => $categoryId]);
|
||||
$cacheHandler->set($cacheKey, $sortType);
|
||||
|
||||
return $sortType;
|
||||
}
|
||||
|
||||
public function categoryName(int $categoryId, string $langId): string
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "category_name:{$langId}:{$categoryId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (string)unserialize($objectData);
|
||||
}
|
||||
|
||||
$name = $this->db->get('pp_shop_categories_langs', 'title', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $name);
|
||||
|
||||
return (string)$name;
|
||||
}
|
||||
|
||||
public function categoryUrl(int $categoryId, string $langId): string
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
$category = $this->frontCategoryDetails($categoryId, $langId);
|
||||
if (empty($category)) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
$url = !empty($category['language']['seo_link'])
|
||||
? '/' . $category['language']['seo_link']
|
||||
: '/k-' . $category['id'] . '-' . \Shared\Helpers\Helpers::seo($category['language']['title'] ?? '');
|
||||
|
||||
$currentLang = \Shared\Helpers\Helpers::get_session('current-lang');
|
||||
$defaultLang = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
|
||||
|
||||
if ($currentLang != $defaultLang && $url !== '#') {
|
||||
$url = '/' . $currentLang . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function frontCategoryDetails(int $categoryId, string $langId): array
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "front_category_details:{$categoryId}:{$langId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $categoryId]);
|
||||
if (!is_array($category)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$category['language'] = $this->db->get('pp_shop_categories_langs', '*', [
|
||||
'AND' => [
|
||||
'category_id' => $categoryId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $category);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoriesTree(string $langId, ?int $parentId = null): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "categories_tree:{$langId}:{$parentId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
$results = $this->db->select('pp_shop_categories', 'id', [
|
||||
'parent_id' => $parentId,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$category = $this->frontCategoryDetails((int)$row, $langId);
|
||||
$category['categories'] = $this->categoriesTree($langId, (int)$row);
|
||||
$categories[] = $category;
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $categories);
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function blogCategoryProducts(int $categoryId, string $langId, int $limit): array
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '' || $limit <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "blog_category_products:{$categoryId}:{$langId}:{$limit}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT * FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ' '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. 'RAND() '
|
||||
. 'LIMIT ' . (int)$limit,
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
]
|
||||
);
|
||||
|
||||
$output = [];
|
||||
if ($rows) {
|
||||
foreach ($rows->fetchAll() as $row) {
|
||||
$output[] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function categoryProductsCount(int $categoryId, string $langId): int
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "category_products_count:{$categoryId}:{$langId}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return (int)unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT COUNT(0) FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ' '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL',
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
]
|
||||
);
|
||||
|
||||
$productsCount = 0;
|
||||
if ($rows) {
|
||||
$result = $rows->fetchAll();
|
||||
$productsCount = (int)($result[0][0] ?? 0);
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $productsCount);
|
||||
|
||||
return $productsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function productsId(int $categoryId, int $sortType, string $langId, int $productsLimit, int $from): array
|
||||
{
|
||||
if ($categoryId <= 0 || $langId === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$order = self::SORT_ORDER_SQL[$sortType] ?? self::SORT_ORDER_SQL[0];
|
||||
$today = date('Y-m-d');
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "products_id:{$categoryId}:{$sortType}:{$langId}:{$productsLimit}:{$from}";
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$rows = $this->db->query(
|
||||
'SELECT * FROM ('
|
||||
. 'SELECT '
|
||||
. 'psp.id, date_modify, date_add, o, '
|
||||
. self::LANGUAGE_FALLBACK_NAME_SQL . ', '
|
||||
. '(CASE '
|
||||
. 'WHEN new_to_date >= :today THEN new_to_date '
|
||||
. 'WHEN new_to_date < :today2 THEN null '
|
||||
. 'END) AS new_to_date, '
|
||||
. '(CASE WHEN (quantity + (SELECT IFNULL(SUM(quantity),0) FROM pp_shop_products WHERE parent_id = psp.id)) > 0 THEN 1 ELSE 0 END) AS total_quantity '
|
||||
. 'FROM '
|
||||
. 'pp_shop_products_categories AS pspc '
|
||||
. 'INNER JOIN pp_shop_products AS psp ON psp.id = pspc.product_id '
|
||||
. 'INNER JOIN pp_shop_products_langs AS pspl ON pspl.product_id = pspc.product_id '
|
||||
. 'WHERE '
|
||||
. 'status = 1 AND category_id = :category_id AND lang_id = :lang_id'
|
||||
. ') AS q1 '
|
||||
. 'WHERE '
|
||||
. 'q1.name IS NOT NULL '
|
||||
. 'ORDER BY '
|
||||
. $order . ' '
|
||||
. 'LIMIT ' . (int)$from . ',' . (int)$productsLimit,
|
||||
[
|
||||
':category_id' => $categoryId,
|
||||
':lang_id' => $langId,
|
||||
':today' => $today,
|
||||
':today2' => $today,
|
||||
]
|
||||
);
|
||||
|
||||
$output = [];
|
||||
if ($rows) {
|
||||
foreach ($rows->fetchAll() as $row) {
|
||||
$output[] = $row['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{products: array, ls: int}
|
||||
*/
|
||||
public function paginatedCategoryProducts(int $categoryId, int $sortType, string $langId, int $page): array
|
||||
{
|
||||
$count = $this->categoryProductsCount($categoryId, $langId);
|
||||
if ($count <= 0) {
|
||||
return ['products' => [], 'ls' => 0];
|
||||
}
|
||||
|
||||
$totalPages = (int)ceil($count / self::PRODUCTS_PER_PAGE);
|
||||
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
} elseif ($page > $totalPages) {
|
||||
$page = $totalPages;
|
||||
}
|
||||
|
||||
$from = self::PRODUCTS_PER_PAGE * ($page - 1);
|
||||
|
||||
return [
|
||||
'products' => $this->productsId($categoryId, $sortType, $langId, self::PRODUCTS_PER_PAGE, $from),
|
||||
'ls' => $totalPages,
|
||||
];
|
||||
}
|
||||
|
||||
private function maxOrder(): int
|
||||
{
|
||||
return (int)$this->db->max('pp_shop_categories', 'o');
|
||||
}
|
||||
|
||||
private function refreshCategoryArtifacts(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
}
|
||||
|
||||
private function normalizeSeoLink($value): ?string
|
||||
{
|
||||
$seo = \Shared\Helpers\Helpers::seo((string)$value);
|
||||
$seo = trim((string)$seo);
|
||||
|
||||
return $seo !== '' ? $seo : null;
|
||||
}
|
||||
|
||||
private function toNullableString($value): ?string
|
||||
{
|
||||
$text = trim((string)$value);
|
||||
return $text === '' ? null : $text;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function toNullableInt($value): ?int
|
||||
{
|
||||
if ($value === null || $value === '' || (int)$value <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
private function defaultCategory(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'status' => 1,
|
||||
'parent_id' => null,
|
||||
'sort_type' => 0,
|
||||
'view_subcategories' => 0,
|
||||
'languages' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function productName(int $productId): string
|
||||
{
|
||||
if ($productId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$defaultLang = $this->db->get('pp_langs', 'id', ['start' => 1]);
|
||||
if (!$defaultLang) {
|
||||
$defaultLang = 'pl';
|
||||
}
|
||||
|
||||
$name = $this->db->get('pp_shop_products_langs', 'name', [
|
||||
'AND' => [
|
||||
'product_id' => $productId,
|
||||
'lang_id' => (string)$defaultLang,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($name !== false && $name !== null && trim((string)$name) !== '') {
|
||||
return (string)$name;
|
||||
}
|
||||
|
||||
$fallback = $this->db->get('pp_shop_products_langs', 'name', [
|
||||
'AND' => [
|
||||
'product_id' => $productId,
|
||||
'name[!]' => '',
|
||||
],
|
||||
'LIMIT' => 1,
|
||||
]);
|
||||
|
||||
return $fallback ? (string)$fallback : '';
|
||||
}
|
||||
|
||||
public function getCategoryProductIds(int $categoryId): array
|
||||
{
|
||||
if ($categoryId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = $this->db->select('pp_shop_products_categories', 'product_id', ['category_id' => $categoryId]);
|
||||
|
||||
return is_array($result) ? $result : [];
|
||||
}
|
||||
|
||||
}
|
||||
532
autoload/Domain/Client/ClientRepository.php
Normal file
532
autoload/Domain/Client/ClientRepository.php
Normal file
@@ -0,0 +1,532 @@
|
||||
<?php
|
||||
namespace Domain\Client;
|
||||
|
||||
class ClientRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'client_surname',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'client_name' => 'c.client_name',
|
||||
'client_surname' => 'c.client_surname',
|
||||
'client_email' => 'c.client_email',
|
||||
'client_phone' => 'c.client_phone',
|
||||
'client_city' => 'c.client_city',
|
||||
'total_orders' => 'c.total_orders',
|
||||
'total_spent' => 'c.total_spent',
|
||||
'client_type' => 'c.is_registered',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'c.client_surname';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = [
|
||||
'o.client_name IS NOT NULL',
|
||||
'o.client_surname IS NOT NULL',
|
||||
'o.client_email IS NOT NULL',
|
||||
];
|
||||
$params = [];
|
||||
|
||||
$name = $this->normalizeTextFilter($filters['name'] ?? '');
|
||||
if ($name !== '') {
|
||||
$where[] = 'o.client_name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$surname = $this->normalizeTextFilter($filters['surname'] ?? '');
|
||||
if ($surname !== '') {
|
||||
$where[] = 'o.client_surname LIKE :surname';
|
||||
$params[':surname'] = '%' . $surname . '%';
|
||||
}
|
||||
|
||||
$email = $this->normalizeTextFilter($filters['email'] ?? '');
|
||||
if ($email !== '') {
|
||||
$where[] = 'o.client_email LIKE :email';
|
||||
$params[':email'] = '%' . $email . '%';
|
||||
}
|
||||
|
||||
$clientType = trim((string)($filters['client_type'] ?? ''));
|
||||
if ($clientType === 'registered') {
|
||||
$where[] = 'o.client_id IS NOT NULL';
|
||||
} elseif ($clientType === 'guest') {
|
||||
$where[] = 'o.client_id IS NULL';
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$aggregatedSql = "
|
||||
SELECT
|
||||
MAX(o.client_id) AS client_id,
|
||||
o.client_name,
|
||||
o.client_surname,
|
||||
o.client_email,
|
||||
MAX(o.client_phone) AS client_phone,
|
||||
MAX(o.client_city) AS client_city,
|
||||
COUNT(*) AS total_orders,
|
||||
COALESCE(SUM(o.summary), 0) AS total_spent,
|
||||
CASE
|
||||
WHEN MAX(o.client_id) IS NOT NULL THEN 1
|
||||
ELSE 0
|
||||
END AS is_registered
|
||||
FROM pp_shop_orders AS o
|
||||
WHERE {$whereSql}
|
||||
GROUP BY o.client_name, o.client_surname, o.client_email
|
||||
";
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM ({$aggregatedSql}) AS c
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
c.client_id,
|
||||
c.client_name,
|
||||
c.client_surname,
|
||||
c.client_email,
|
||||
c.client_phone,
|
||||
c.client_city,
|
||||
c.total_orders,
|
||||
c.total_spent,
|
||||
c.is_registered
|
||||
FROM ({$aggregatedSql}) AS c
|
||||
ORDER BY {$sortSql} {$sortDir}, c.client_surname ASC, c.client_name ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['client_id'] = !isset($item['client_id']) ? null : (int)$item['client_id'];
|
||||
$item['client_name'] = (string)($item['client_name'] ?? '');
|
||||
$item['client_surname'] = (string)($item['client_surname'] ?? '');
|
||||
$item['client_email'] = (string)($item['client_email'] ?? '');
|
||||
$item['client_phone'] = (string)($item['client_phone'] ?? '');
|
||||
$item['client_city'] = (string)($item['client_city'] ?? '');
|
||||
$item['total_orders'] = (int)($item['total_orders'] ?? 0);
|
||||
$item['total_spent'] = (float)($item['total_spent'] ?? 0);
|
||||
$item['is_registered'] = ((int)($item['is_registered'] ?? 0)) === 1 ? 1 : 0;
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function ordersForClient(string $name, string $surname, string $email): array
|
||||
{
|
||||
$name = trim($name);
|
||||
$surname = trim($surname);
|
||||
$email = trim($email);
|
||||
|
||||
if ($name === '' || $surname === '' || $email === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
o.id,
|
||||
o.date_order,
|
||||
o.summary,
|
||||
o.payment_method,
|
||||
o.transport,
|
||||
o.message
|
||||
FROM pp_shop_orders AS o
|
||||
WHERE o.client_name = :name
|
||||
AND o.client_surname = :surname
|
||||
AND o.client_email = :email
|
||||
ORDER BY o.date_order DESC, o.id DESC
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, [
|
||||
':name' => $name,
|
||||
':surname' => $surname,
|
||||
':email' => $email,
|
||||
]);
|
||||
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$row['id'] = (int)($row['id'] ?? 0);
|
||||
$row['date_order'] = (string)($row['date_order'] ?? '');
|
||||
$row['summary'] = (float)($row['summary'] ?? 0);
|
||||
$row['payment_method'] = (string)($row['payment_method'] ?? '');
|
||||
$row['transport'] = (string)($row['transport'] ?? '');
|
||||
$row['message'] = (string)($row['message'] ?? '');
|
||||
}
|
||||
unset($row);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{total_orders: int, total_spent: float}
|
||||
*/
|
||||
public function totalsForClient(string $name, string $surname, string $email): array
|
||||
{
|
||||
$name = trim($name);
|
||||
$surname = trim($surname);
|
||||
$email = trim($email);
|
||||
|
||||
if ($name === '' || $surname === '' || $email === '') {
|
||||
return [
|
||||
'total_orders' => 0,
|
||||
'total_spent' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
COUNT(*) AS total_orders,
|
||||
COALESCE(SUM(o.summary), 0) AS total_spent
|
||||
FROM pp_shop_orders AS o
|
||||
WHERE o.client_name = :name
|
||||
AND o.client_surname = :surname
|
||||
AND o.client_email = :email
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, [
|
||||
':name' => $name,
|
||||
':surname' => $surname,
|
||||
':email' => $email,
|
||||
]);
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'total_orders' => isset($rows[0]['total_orders']) ? (int)$rows[0]['total_orders'] : 0,
|
||||
'total_spent' => isset($rows[0]['total_spent']) ? (float)$rows[0]['total_spent'] : 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
// ===== Frontend methods =====
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function clientDetails(int $clientId): ?array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db->get('pp_shop_clients', '*', ['id' => $clientId]) ?: null;
|
||||
}
|
||||
|
||||
public function clientEmail(int $clientId): ?string
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$email = $this->db->get('pp_shop_clients', 'email', ['id' => $clientId]);
|
||||
|
||||
return $email ? (string)$email : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function clientAddresses(int $clientId): array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_shop_clients_addresses', '*', ['client_id' => $clientId]);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function addressDetails(int $addressId): ?array
|
||||
{
|
||||
if ($addressId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->db->get('pp_shop_clients_addresses', '*', ['id' => $addressId]) ?: null;
|
||||
}
|
||||
|
||||
public function addressDelete(int $addressId): bool
|
||||
{
|
||||
if ($addressId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_shop_clients_addresses', ['id' => $addressId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $data Keys: name, surname, street, postal_code, city, phone
|
||||
*/
|
||||
public function addressSave(int $clientId, ?int $addressId, array $data): bool
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'name' => (string)($data['name'] ?? ''),
|
||||
'surname' => (string)($data['surname'] ?? ''),
|
||||
'street' => (string)($data['street'] ?? ''),
|
||||
'postal_code' => (string)($data['postal_code'] ?? ''),
|
||||
'city' => (string)($data['city'] ?? ''),
|
||||
'phone' => (string)($data['phone'] ?? ''),
|
||||
];
|
||||
|
||||
if (!$addressId || $addressId <= 0) {
|
||||
$row['client_id'] = $clientId;
|
||||
return (bool)$this->db->insert('pp_shop_clients_addresses', $row);
|
||||
}
|
||||
|
||||
return (bool)$this->db->update('pp_shop_clients_addresses', $row, [
|
||||
'AND' => [
|
||||
'client_id' => $clientId,
|
||||
'id' => $addressId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function markAddressAsCurrent(int $clientId, int $addressId): bool
|
||||
{
|
||||
if ($clientId <= 0 || $addressId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients_addresses', ['current' => 0], ['client_id' => $clientId]);
|
||||
$this->db->update('pp_shop_clients_addresses', ['current' => 1], [
|
||||
'AND' => [
|
||||
'client_id' => $clientId,
|
||||
'id' => $addressId,
|
||||
],
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function clientOrders(int $clientId): array
|
||||
{
|
||||
if ($clientId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_shop_orders', 'id', [
|
||||
'client_id' => $clientId,
|
||||
'ORDER' => ['date_order' => 'DESC'],
|
||||
]);
|
||||
|
||||
$orders = [];
|
||||
if (is_array($rows)) {
|
||||
$orderRepo = new \Domain\Order\OrderRepository($this->db);
|
||||
foreach ($rows as $row) {
|
||||
$orders[] = $orderRepo->orderDetailsFrontend($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status: string, client?: array, hash?: string, code?: string}
|
||||
*/
|
||||
public function authenticate(string $email, string $password): array
|
||||
{
|
||||
$email = trim($email);
|
||||
$password = trim($password);
|
||||
|
||||
if ($email === '' || $password === '') {
|
||||
return ['status' => 'error', 'code' => 'logowanie-nieudane'];
|
||||
}
|
||||
|
||||
$client = $this->db->get('pp_shop_clients', [
|
||||
'id', 'password', 'register_date', 'hash', 'status',
|
||||
], ['email' => $email]);
|
||||
|
||||
if (!$client) {
|
||||
return ['status' => 'error', 'code' => 'logowanie-nieudane'];
|
||||
}
|
||||
|
||||
if (!(int)$client['status']) {
|
||||
return ['status' => 'inactive', 'hash' => $client['hash']];
|
||||
}
|
||||
|
||||
if ($client['password'] !== md5($client['register_date'] . $password)) {
|
||||
return ['status' => 'error', 'code' => 'logowanie-blad-nieprawidlowe-haslo'];
|
||||
}
|
||||
|
||||
$fullClient = $this->clientDetails((int)$client['id']);
|
||||
|
||||
return ['status' => 'ok', 'client' => $fullClient];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{id: int, hash: string}|null Null when email already taken
|
||||
*/
|
||||
public function createClient(string $email, string $password, bool $agreementMarketing): ?array
|
||||
{
|
||||
$email = trim($email);
|
||||
if ($email === '' || $password === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->db->count('pp_shop_clients', ['email' => $email])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = md5(time() . $email);
|
||||
$registerDate = date('Y-m-d H:i:s');
|
||||
|
||||
$inserted = $this->db->insert('pp_shop_clients', [
|
||||
'email' => $email,
|
||||
'password' => md5($registerDate . $password),
|
||||
'hash' => $hash,
|
||||
'agremment_marketing' => $agreementMarketing ? 1 : 0,
|
||||
'register_date' => $registerDate,
|
||||
]);
|
||||
|
||||
if (!$inserted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int)$this->db->id(),
|
||||
'hash' => $hash,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms registration. Returns client email on success, null on failure.
|
||||
*/
|
||||
public function confirmRegistration(string $hash): ?string
|
||||
{
|
||||
$hash = trim($hash);
|
||||
if ($hash === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$id = $this->db->get('pp_shop_clients', 'id', [
|
||||
'AND' => ['hash' => $hash, 'status' => 0],
|
||||
]);
|
||||
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients', ['status' => 1], ['id' => $id]);
|
||||
|
||||
$email = $this->db->get('pp_shop_clients', 'email', ['id' => $id]);
|
||||
|
||||
return $email ? (string)$email : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new password. Returns [email, password] on success, null on failure.
|
||||
*
|
||||
* @return array{email: string, password: string}|null
|
||||
*/
|
||||
public function generateNewPassword(string $hash): ?array
|
||||
{
|
||||
$hash = trim($hash);
|
||||
if ($hash === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $this->db->get('pp_shop_clients', ['id', 'email', 'register_date'], [
|
||||
'AND' => ['hash' => $hash, 'status' => 1, 'password_recovery' => 1],
|
||||
]);
|
||||
|
||||
if (!$data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$newPassword = substr(md5(time()), 0, 10);
|
||||
|
||||
$this->db->update('pp_shop_clients', [
|
||||
'password_recovery' => 0,
|
||||
'password' => md5($data['register_date'] . $newPassword),
|
||||
], ['id' => $data['id']]);
|
||||
|
||||
return [
|
||||
'email' => (string)$data['email'],
|
||||
'password' => $newPassword,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates password recovery. Returns hash on success, null on failure.
|
||||
*/
|
||||
public function initiatePasswordRecovery(string $email): ?string
|
||||
{
|
||||
$email = trim($email);
|
||||
if ($email === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hash = $this->db->get('pp_shop_clients', 'hash', [
|
||||
'AND' => ['email' => $email, 'status' => 1],
|
||||
]);
|
||||
|
||||
if (!$hash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_clients', ['password_recovery' => 1], ['email' => $email]);
|
||||
|
||||
return (string)$hash;
|
||||
}
|
||||
|
||||
private function normalizeTextFilter($value): string
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (strlen($value) > 255) {
|
||||
return substr($value, 0, 255);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
458
autoload/Domain/Coupon/CouponRepository.php
Normal file
458
autoload/Domain/Coupon/CouponRepository.php
Normal file
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
namespace Domain\Coupon;
|
||||
|
||||
class CouponRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
private ?string $defaultLangId = null;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'sc.id',
|
||||
'status' => 'sc.status',
|
||||
'used_count' => 'sc.used_count',
|
||||
'name' => 'sc.name',
|
||||
'type' => 'sc.type',
|
||||
'amount' => 'sc.amount',
|
||||
'one_time' => 'sc.one_time',
|
||||
'send' => 'sc.send',
|
||||
'used' => 'sc.used',
|
||||
'date_used' => 'sc.date_used',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'sc.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'sc.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'sc.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$used = trim((string)($filters['used'] ?? ''));
|
||||
if ($used === '0' || $used === '1') {
|
||||
$where[] = 'sc.used = :used';
|
||||
$params[':used'] = (int)$used;
|
||||
}
|
||||
|
||||
$send = trim((string)($filters['send'] ?? ''));
|
||||
if ($send === '0' || $send === '1') {
|
||||
$where[] = 'sc.send = :send';
|
||||
$params[':send'] = (int)$send;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_coupon AS sc
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
sc.id,
|
||||
sc.name,
|
||||
sc.status,
|
||||
sc.used,
|
||||
sc.type,
|
||||
sc.amount,
|
||||
sc.one_time,
|
||||
sc.send,
|
||||
sc.include_discounted_product,
|
||||
sc.categories,
|
||||
sc.date_used,
|
||||
sc.used_count
|
||||
FROM pp_shop_coupon AS sc
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, sc.id DESC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['id'] = (int)($item['id'] ?? 0);
|
||||
$item['status'] = $this->toSwitchValue($item['status'] ?? 0);
|
||||
$item['used'] = $this->toSwitchValue($item['used'] ?? 0);
|
||||
$item['one_time'] = $this->toSwitchValue($item['one_time'] ?? 0);
|
||||
$item['send'] = $this->toSwitchValue($item['send'] ?? 0);
|
||||
$item['used_count'] = (int)($item['used_count'] ?? 0);
|
||||
$item['type'] = (int)($item['type'] ?? 1);
|
||||
$item['categories'] = $this->decodeIdList($item['categories'] ?? null);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $couponId): array
|
||||
{
|
||||
if ($couponId <= 0) {
|
||||
return $this->defaultCoupon();
|
||||
}
|
||||
|
||||
$coupon = $this->db->get('pp_shop_coupon', '*', ['id' => $couponId]);
|
||||
if (!is_array($coupon)) {
|
||||
return $this->defaultCoupon();
|
||||
}
|
||||
|
||||
$coupon['id'] = (int)($coupon['id'] ?? 0);
|
||||
$coupon['status'] = $this->toSwitchValue($coupon['status'] ?? 0);
|
||||
$coupon['send'] = $this->toSwitchValue($coupon['send'] ?? 0);
|
||||
$coupon['used'] = $this->toSwitchValue($coupon['used'] ?? 0);
|
||||
$coupon['one_time'] = $this->toSwitchValue($coupon['one_time'] ?? 0);
|
||||
$coupon['include_discounted_product'] = $this->toSwitchValue($coupon['include_discounted_product'] ?? 0);
|
||||
$coupon['type'] = (int)($coupon['type'] ?? 1);
|
||||
$coupon['used_count'] = (int)($coupon['used_count'] ?? 0);
|
||||
$coupon['categories'] = $this->decodeIdList($coupon['categories'] ?? null);
|
||||
|
||||
return $coupon;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$couponId = (int)($data['id'] ?? 0);
|
||||
|
||||
$row = [
|
||||
'name' => trim((string)($data['name'] ?? '')),
|
||||
'status' => $this->toSwitchValue($data['status'] ?? 0),
|
||||
'send' => $this->toSwitchValue($data['send'] ?? 0),
|
||||
'used' => $this->toSwitchValue($data['used'] ?? 0),
|
||||
'type' => (int)($data['type'] ?? 1),
|
||||
'amount' => $this->toNullableNumeric($data['amount'] ?? null),
|
||||
'one_time' => $this->toSwitchValue($data['one_time'] ?? 0),
|
||||
'include_discounted_product' => $this->toSwitchValue($data['include_discounted_product'] ?? 0),
|
||||
'categories' => $this->encodeIdList($data['categories'] ?? null),
|
||||
];
|
||||
|
||||
if ($couponId <= 0) {
|
||||
$this->db->insert('pp_shop_coupon', $row);
|
||||
$id = (int)$this->db->id();
|
||||
return $id > 0 ? $id : null;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_coupon', $row, ['id' => $couponId]);
|
||||
return $couponId;
|
||||
}
|
||||
|
||||
public function delete(int $couponId): bool
|
||||
{
|
||||
if ($couponId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_shop_coupon', ['id' => $couponId]);
|
||||
}
|
||||
|
||||
public function findByName(string $name)
|
||||
{
|
||||
$name = trim($name);
|
||||
if ($name === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$coupon = $this->db->get('pp_shop_coupon', '*', ['name' => $name]);
|
||||
if (!is_array($coupon)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$coupon['id'] = (int)($coupon['id'] ?? 0);
|
||||
$coupon['status'] = (int)($coupon['status'] ?? 0);
|
||||
$coupon['used'] = (int)($coupon['used'] ?? 0);
|
||||
$coupon['one_time'] = (int)($coupon['one_time'] ?? 0);
|
||||
$coupon['type'] = (int)($coupon['type'] ?? 0);
|
||||
$coupon['include_discounted_product'] = (int)($coupon['include_discounted_product'] ?? 0);
|
||||
$coupon['used_count'] = (int)($coupon['used_count'] ?? 0);
|
||||
|
||||
return (object)$coupon;
|
||||
}
|
||||
|
||||
public function isAvailable($coupon)
|
||||
{
|
||||
if (!$coupon) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = is_object($coupon) ? ($coupon->id ?? 0) : ($coupon['id'] ?? 0);
|
||||
$status = is_object($coupon) ? ($coupon->status ?? 0) : ($coupon['status'] ?? 0);
|
||||
$used = is_object($coupon) ? ($coupon->used ?? 0) : ($coupon['used'] ?? 0);
|
||||
|
||||
if (!(int)$id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(int)$status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(int)$used;
|
||||
}
|
||||
|
||||
public function markAsUsed(int $couponId)
|
||||
{
|
||||
if ($couponId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_coupon', [
|
||||
'used' => 1,
|
||||
'date_used' => date('Y-m-d H:i:s'),
|
||||
], ['id' => $couponId]);
|
||||
}
|
||||
|
||||
public function incrementUsedCount(int $couponId)
|
||||
{
|
||||
if ($couponId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_coupon', [
|
||||
'used_count[+]' => 1,
|
||||
], ['id' => $couponId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoriesTree($parentId = null): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_categories', ['id'], [
|
||||
'parent_id' => $parentId,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach ($rows as $row) {
|
||||
$categoryId = (int)($row['id'] ?? 0);
|
||||
if ($categoryId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $categoryId]);
|
||||
if (!is_array($category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations = $this->db->select('pp_shop_categories_langs', '*', ['category_id' => $categoryId]);
|
||||
$category['languages'] = [];
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $translation) {
|
||||
$langId = (string)($translation['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$category['languages'][$langId] = $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$category['title'] = $this->categoryTitle($category['languages']);
|
||||
$category['subcategories'] = $this->categoriesTree($categoryId);
|
||||
$categories[] = $category;
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
private function defaultCoupon(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
'send' => 0,
|
||||
'used' => 0,
|
||||
'type' => 1,
|
||||
'amount' => null,
|
||||
'one_time' => 1,
|
||||
'include_discounted_product' => 0,
|
||||
'categories' => [],
|
||||
'used_count' => 0,
|
||||
'date_used' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function toNullableNumeric($value): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stringValue = trim((string)$value);
|
||||
if ($stringValue === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str_replace(',', '.', $stringValue);
|
||||
}
|
||||
|
||||
private function encodeIdList($values): ?string
|
||||
{
|
||||
$ids = $this->normalizeIdList($values);
|
||||
if (empty($ids)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_encode($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function decodeIdList($raw): array
|
||||
{
|
||||
if (is_array($raw)) {
|
||||
return $this->normalizeIdList($raw);
|
||||
}
|
||||
|
||||
$text = trim((string)$raw);
|
||||
if ($text === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($text, true);
|
||||
if (!is_array($decoded)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->normalizeIdList($decoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function normalizeIdList($values): array
|
||||
{
|
||||
if ($values === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!is_array($values)) {
|
||||
$text = trim((string)$values);
|
||||
if ($text === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (strpos($text, ',') !== false) {
|
||||
$values = explode(',', $text);
|
||||
} else {
|
||||
$values = [$text];
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($values as $value) {
|
||||
$id = (int)$value;
|
||||
if ($id > 0) {
|
||||
$ids[$id] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($ids);
|
||||
}
|
||||
|
||||
private function categoryTitle(array $languages): string
|
||||
{
|
||||
$defaultLang = $this->defaultLanguageId();
|
||||
if ($defaultLang !== '' && isset($languages[$defaultLang]['title'])) {
|
||||
$title = trim((string)$languages[$defaultLang]['title']);
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$title = trim((string)($language['title'] ?? ''));
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function defaultLanguageId(): string
|
||||
{
|
||||
if ($this->defaultLangId !== null) {
|
||||
return $this->defaultLangId;
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_langs', ['id', 'start', 'o'], [
|
||||
'status' => 1,
|
||||
'ORDER' => ['start' => 'DESC', 'o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (is_array($rows) && !empty($rows)) {
|
||||
$this->defaultLangId = (string)($rows[0]['id'] ?? '');
|
||||
} else {
|
||||
$this->defaultLangId = '';
|
||||
}
|
||||
|
||||
return $this->defaultLangId;
|
||||
}
|
||||
}
|
||||
|
||||
153
autoload/Domain/Dashboard/DashboardRepository.php
Normal file
153
autoload/Domain/Dashboard/DashboardRepository.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
namespace Domain\Dashboard;
|
||||
|
||||
class DashboardRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function summaryOrders(): int
|
||||
{
|
||||
try {
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
||||
if ( $redis ) {
|
||||
$cached = $redis->get( 'summary_ordersd' );
|
||||
if ( $cached !== false ) {
|
||||
return (int) unserialize( $cached );
|
||||
}
|
||||
$summary = (int) $this->db->count( 'pp_shop_orders', [ 'status' => 6 ] );
|
||||
$redis->setex( 'summary_ordersd', 300, serialize( $summary ) );
|
||||
return $summary;
|
||||
}
|
||||
} catch ( \RedisException $e ) {
|
||||
// fallback
|
||||
}
|
||||
|
||||
return (int) $this->db->count( 'pp_shop_orders', [ 'status' => 6 ] );
|
||||
}
|
||||
|
||||
public function summarySales(): float
|
||||
{
|
||||
try {
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
||||
if ( $redis ) {
|
||||
$cached = $redis->get( 'summary_salesd' );
|
||||
if ( $cached !== false ) {
|
||||
return (float) unserialize( $cached );
|
||||
}
|
||||
$summary = $this->calculateTotalSales();
|
||||
$redis->setex( 'summary_salesd', 300, serialize( $summary ) );
|
||||
return $summary;
|
||||
}
|
||||
} catch ( \RedisException $e ) {
|
||||
// fallback
|
||||
}
|
||||
|
||||
return $this->calculateTotalSales();
|
||||
}
|
||||
|
||||
private function calculateTotalSales(): float
|
||||
{
|
||||
return (float) $this->db->sum( 'pp_shop_orders', 'summary', [ 'status' => 6 ] )
|
||||
- (float) $this->db->sum( 'pp_shop_orders', 'transport_cost', [ 'status' => 6 ] );
|
||||
}
|
||||
|
||||
public function salesGrid(): array
|
||||
{
|
||||
$grid = [];
|
||||
$rows = $this->db->select( 'pp_shop_orders', [ 'id', 'date_order' ], [ 'status' => 6 ] );
|
||||
|
||||
if ( is_array( $rows ) ) {
|
||||
foreach ( $rows as $row ) {
|
||||
$ts = strtotime( $row['date_order'] );
|
||||
$dayOfWeek = date( 'N', $ts );
|
||||
$hour = date( 'G', $ts );
|
||||
if ( !isset( $grid[$dayOfWeek][$hour] ) ) {
|
||||
$grid[$dayOfWeek][$hour] = 0;
|
||||
}
|
||||
$grid[$dayOfWeek][$hour]++;
|
||||
}
|
||||
}
|
||||
|
||||
return $grid;
|
||||
}
|
||||
|
||||
public function mostViewedProducts(): array
|
||||
{
|
||||
$stmt = $this->db->query(
|
||||
'SELECT id, SUM(visits) AS visits '
|
||||
. 'FROM pp_shop_products '
|
||||
. 'GROUP BY id '
|
||||
. 'ORDER BY visits DESC '
|
||||
. 'LIMIT 10'
|
||||
);
|
||||
|
||||
return $stmt ? $stmt->fetchAll( \PDO::FETCH_ASSOC ) : [];
|
||||
}
|
||||
|
||||
public function bestSalesProducts(): array
|
||||
{
|
||||
$stmt = $this->db->query(
|
||||
'SELECT parent_product_id, SUM(quantity) AS quantity_summary, SUM(price_brutto_promo * quantity) AS sales '
|
||||
. 'FROM pp_shop_order_products AS psop '
|
||||
. 'INNER JOIN pp_shop_orders AS pso ON pso.id = psop.order_id '
|
||||
. 'WHERE pso.status = 6 '
|
||||
. 'GROUP BY parent_product_id '
|
||||
. 'ORDER BY sales DESC '
|
||||
. 'LIMIT 10'
|
||||
);
|
||||
|
||||
return $stmt ? $stmt->fetchAll( \PDO::FETCH_ASSOC ) : [];
|
||||
}
|
||||
|
||||
public function last24MonthsSales(): array
|
||||
{
|
||||
$sales = [];
|
||||
$date = new \DateTime();
|
||||
|
||||
for ( $i = 0; $i < 24; $i++ ) {
|
||||
$dateStart = $date->format( 'Y-m-01' );
|
||||
$dateEnd = $date->format( 'Y-m-t' );
|
||||
|
||||
$where = [
|
||||
'AND' => [
|
||||
'status' => 6,
|
||||
'date_order[>=]' => $dateStart,
|
||||
'date_order[<=]' => $dateEnd,
|
||||
]
|
||||
];
|
||||
|
||||
$monthSales = (float) $this->db->sum( 'pp_shop_orders', 'summary', $where )
|
||||
- (float) $this->db->sum( 'pp_shop_orders', 'transport_cost', $where );
|
||||
|
||||
$sales[] = [
|
||||
'date' => $date->format( 'Y-m' ),
|
||||
'sales' => $monthSales,
|
||||
];
|
||||
|
||||
$date->sub( new \DateInterval( 'P1M' ) );
|
||||
}
|
||||
|
||||
return $sales;
|
||||
}
|
||||
|
||||
public function lastOrders( int $limit = 10 ): array
|
||||
{
|
||||
$stmt = $this->db->query(
|
||||
'SELECT id, number, date_order, '
|
||||
. 'CONCAT( client_name, \' \', client_surname ) AS client, '
|
||||
. 'client_email, '
|
||||
. 'CONCAT( client_street, \', \', client_postal_code, \' \', client_city ) AS address, '
|
||||
. 'status, client_phone, summary '
|
||||
. 'FROM pp_shop_orders '
|
||||
. 'ORDER BY date_order DESC '
|
||||
. 'LIMIT ' . (int) $limit
|
||||
);
|
||||
|
||||
return $stmt ? $stmt->fetchAll( \PDO::FETCH_ASSOC ) : [];
|
||||
}
|
||||
}
|
||||
270
autoload/Domain/Dictionaries/DictionariesRepository.php
Normal file
270
autoload/Domain/Dictionaries/DictionariesRepository.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
namespace Domain\Dictionaries;
|
||||
|
||||
class DictionariesRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
private const CACHE_TTL = 86400;
|
||||
private const CACHE_SUBDIR = 'dictionaries';
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'id',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'u.id',
|
||||
'text' => 'text',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'u.id';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1=1'];
|
||||
$params = [];
|
||||
|
||||
$text = trim((string)($filters['text'] ?? ''));
|
||||
if (strlen($text) > 255) {
|
||||
$text = substr($text, 0, 255);
|
||||
}
|
||||
|
||||
if ($text !== '') {
|
||||
$where[] = 'EXISTS (SELECT 1 FROM pp_units_langs AS ul2 WHERE ul2.unit_id = u.id AND ul2.text LIKE :text)';
|
||||
$params[':text'] = '%' . $text . '%';
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_units AS u
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
u.*,
|
||||
(
|
||||
SELECT ul.text
|
||||
FROM pp_units_langs AS ul
|
||||
INNER JOIN pp_langs AS l ON l.id = ul.lang_id
|
||||
WHERE ul.unit_id = u.id AND ul.text <> ''
|
||||
ORDER BY l.o ASC
|
||||
LIMIT 1
|
||||
) AS text
|
||||
FROM pp_units AS u
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, u.id {$sortDir}
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function allUnits(): array
|
||||
{
|
||||
$sql = "
|
||||
SELECT
|
||||
u.*,
|
||||
(
|
||||
SELECT ul.text
|
||||
FROM pp_units_langs AS ul
|
||||
INNER JOIN pp_langs AS l ON l.id = ul.lang_id
|
||||
WHERE ul.unit_id = u.id AND ul.text <> ''
|
||||
ORDER BY l.o ASC
|
||||
LIMIT 1
|
||||
) AS text
|
||||
FROM pp_units AS u
|
||||
ORDER BY u.id ASC
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql);
|
||||
$rows = $stmt ? $stmt->fetchAll(\PDO::FETCH_ASSOC) : [];
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$units = [];
|
||||
foreach ($rows as $row) {
|
||||
$units[(int)$row['id']] = $row;
|
||||
}
|
||||
|
||||
return $units;
|
||||
}
|
||||
|
||||
public function find(int $unitId): ?array
|
||||
{
|
||||
$unit = $this->db->get('pp_units', '*', ['id' => $unitId]);
|
||||
if (!$unit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$unit['languages'] = [];
|
||||
$translations = $this->db->select('pp_units_langs', '*', [
|
||||
'unit_id' => $unitId,
|
||||
'ORDER' => ['lang_id' => 'ASC', 'id' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $row) {
|
||||
$unit['languages'][(string)$row['lang_id']] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $unit;
|
||||
}
|
||||
|
||||
public function save(array $data)
|
||||
{
|
||||
$unitId = isset($data['id']) ? (int)$data['id'] : 0;
|
||||
|
||||
if ($unitId <= 0) {
|
||||
$this->db->insert('pp_units', []);
|
||||
$unitId = (int)$this->db->id();
|
||||
if ($unitId <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$translations = $this->normalizeTranslations($data);
|
||||
foreach ($translations as $langId => $text) {
|
||||
$this->upsertTranslation($unitId, $langId, $text);
|
||||
}
|
||||
|
||||
$this->clearCache();
|
||||
|
||||
return $unitId;
|
||||
}
|
||||
|
||||
public function delete(int $unitId): bool
|
||||
{
|
||||
if ($unitId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->delete('pp_units_langs', ['unit_id' => $unitId]);
|
||||
$result = $this->db->delete('pp_units', ['id' => $unitId]);
|
||||
|
||||
$this->clearCache();
|
||||
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
public function getUnitNameById(int $unitId, $langId): string
|
||||
{
|
||||
$langId = trim((string)$langId);
|
||||
if ($unitId <= 0 || $langId === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$cacheKey = "get_name_by_id:$unitId:$langId";
|
||||
$unitName = $this->cacheFetch($cacheKey);
|
||||
|
||||
if ($unitName === false) {
|
||||
$unitName = $this->db->get('pp_units_langs', 'text', [
|
||||
'AND' => [
|
||||
'unit_id' => $unitId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->cacheStore($cacheKey, $unitName ?: '');
|
||||
}
|
||||
|
||||
return (string)$unitName;
|
||||
}
|
||||
|
||||
private function normalizeTranslations(array $data): array
|
||||
{
|
||||
$translations = [];
|
||||
|
||||
if (isset($data['translations']) && is_array($data['translations'])) {
|
||||
foreach ($data['translations'] as $langId => $fields) {
|
||||
$translations[(string)$langId] = trim((string)($fields['text'] ?? ''));
|
||||
}
|
||||
return $translations;
|
||||
}
|
||||
|
||||
if (isset($data['text']) && is_array($data['text'])) {
|
||||
foreach ($data['text'] as $langId => $text) {
|
||||
$translations[(string)$langId] = trim((string)$text);
|
||||
}
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
private function upsertTranslation(int $unitId, $langId, string $text): void
|
||||
{
|
||||
$langId = trim((string)$langId);
|
||||
if ($langId === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$translationId = $this->db->get('pp_units_langs', 'id', [
|
||||
'AND' => [
|
||||
'unit_id' => $unitId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($translationId) {
|
||||
$this->db->update('pp_units_langs', [
|
||||
'text' => $text,
|
||||
], [
|
||||
'id' => (int)$translationId,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_units_langs', [
|
||||
'unit_id' => $unitId,
|
||||
'lang_id' => $langId,
|
||||
'text' => $text,
|
||||
]);
|
||||
}
|
||||
|
||||
private function clearCache(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/dictionaries');
|
||||
}
|
||||
|
||||
private function cacheFetch(string $key)
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cached = $cacheHandler->get(self::CACHE_SUBDIR . ':' . $key);
|
||||
if ($cached) {
|
||||
return unserialize($cached);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function cacheStore(string $key, string $value): void
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheHandler->set(self::CACHE_SUBDIR . ':' . $key, $value, self::CACHE_TTL);
|
||||
}
|
||||
}
|
||||
712
autoload/Domain/Integrations/IntegrationsRepository.php
Normal file
712
autoload/Domain/Integrations/IntegrationsRepository.php
Normal file
@@ -0,0 +1,712 @@
|
||||
<?php
|
||||
namespace Domain\Integrations;
|
||||
|
||||
class IntegrationsRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
private const SETTINGS_TABLES = [
|
||||
'apilo' => 'pp_shop_apilo_settings',
|
||||
'shoppro' => 'pp_shop_shoppro_settings',
|
||||
];
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
// ── Settings ────────────────────────────────────────────────
|
||||
|
||||
private function settingsTable( string $provider ): string
|
||||
{
|
||||
if ( !isset( self::SETTINGS_TABLES[$provider] ) )
|
||||
throw new \InvalidArgumentException( "Unknown provider: $provider" );
|
||||
|
||||
return self::SETTINGS_TABLES[$provider];
|
||||
}
|
||||
|
||||
public function getSettings( string $provider ): array
|
||||
{
|
||||
$table = $this->settingsTable( $provider );
|
||||
$stmt = $this->db->query( "SELECT * FROM $table" );
|
||||
$results = $stmt ? $stmt->fetchAll( \PDO::FETCH_ASSOC ) : [];
|
||||
$settings = [];
|
||||
foreach ( $results as $row )
|
||||
$settings[$row['name']] = $row['value'];
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
public function getSetting( string $provider, string $name ): ?string
|
||||
{
|
||||
$table = $this->settingsTable( $provider );
|
||||
$value = $this->db->get( $table, 'value', [ 'name' => $name ] );
|
||||
return $value !== false ? $value : null;
|
||||
}
|
||||
|
||||
public function saveSetting( string $provider, string $name, $value ): bool
|
||||
{
|
||||
$table = $this->settingsTable( $provider );
|
||||
if ( $this->db->count( $table, [ 'name' => $name ] ) ) {
|
||||
$this->db->update( $table, [ 'value' => $value ], [ 'name' => $name ] );
|
||||
} else {
|
||||
$this->db->insert( $table, [ 'name' => $name, 'value' => $value ] );
|
||||
}
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── Product linking (Apilo) ─────────────────────────────────
|
||||
|
||||
public function linkProduct( int $productId, $externalId, $externalName ): bool
|
||||
{
|
||||
return (bool) $this->db->update( 'pp_shop_products', [
|
||||
'apilo_product_id' => $externalId,
|
||||
'apilo_product_name' => \Shared\Helpers\Helpers::remove_special_chars( $externalName ),
|
||||
], [ 'id' => $productId ] );
|
||||
}
|
||||
|
||||
public function unlinkProduct( int $productId ): bool
|
||||
{
|
||||
return (bool) $this->db->update( 'pp_shop_products', [
|
||||
'apilo_product_id' => null,
|
||||
'apilo_product_name' => null,
|
||||
], [ 'id' => $productId ] );
|
||||
}
|
||||
|
||||
// ── Apilo OAuth ─────────────────────────────────────────────
|
||||
|
||||
public function apiloAuthorize( string $clientId, string $clientSecret, string $authCode ): bool
|
||||
{
|
||||
$postData = [
|
||||
'grantType' => 'authorization_code',
|
||||
'token' => $authCode,
|
||||
];
|
||||
|
||||
$ch = curl_init( "https://projectpro.apilo.com/rest/auth/token/" );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $postData ) );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Basic " . base64_encode( $clientId . ":" . $clientSecret ),
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
if ( curl_errno( $ch ) ) {
|
||||
curl_close( $ch );
|
||||
return false;
|
||||
}
|
||||
curl_close( $ch );
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
if ( empty( $response['accessToken'] ) )
|
||||
return false;
|
||||
|
||||
$this->saveSetting( 'apilo', 'access-token', $response['accessToken'] );
|
||||
$this->saveSetting( 'apilo', 'refresh-token', $response['refreshToken'] );
|
||||
$this->saveSetting( 'apilo', 'access-token-expire-at', $response['accessTokenExpireAt'] );
|
||||
$this->saveSetting( 'apilo', 'refresh-token-expire-at', $response['refreshTokenExpireAt'] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function apiloGetAccessToken( int $refreshLeadSeconds = 300 ): ?string
|
||||
{
|
||||
$settings = $this->getSettings( 'apilo' );
|
||||
|
||||
$hasRefreshCredentials = !empty( $settings['refresh-token'] )
|
||||
&& !empty( $settings['client-id'] )
|
||||
&& !empty( $settings['client-secret'] );
|
||||
|
||||
$accessToken = trim( (string)($settings['access-token'] ?? '') );
|
||||
$accessTokenExpireAt = trim( (string)($settings['access-token-expire-at'] ?? '') );
|
||||
|
||||
if ( $accessToken !== '' && $accessTokenExpireAt !== '' ) {
|
||||
if ( !$this->shouldRefreshAccessToken( $accessTokenExpireAt, $refreshLeadSeconds ) ) {
|
||||
return $accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$hasRefreshCredentials ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
!empty( $settings['refresh-token-expire-at'] ) &&
|
||||
!$this->isFutureDate( (string)$settings['refresh-token-expire-at'] )
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->refreshApiloAccessToken( $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Keepalive tokenu Apilo do uzycia w CRON.
|
||||
* Odswieza token, gdy wygasa lub jest bliski wygasniecia.
|
||||
*
|
||||
* @return array{success:bool,skipped:bool,message:string}
|
||||
*/
|
||||
public function apiloKeepalive( int $refreshLeadSeconds = 300 ): array
|
||||
{
|
||||
$settings = $this->getSettings( 'apilo' );
|
||||
|
||||
if ( (int)($settings['enabled'] ?? 0) !== 1 ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'skipped' => true,
|
||||
'message' => 'Apilo disabled.',
|
||||
];
|
||||
}
|
||||
|
||||
if ( empty( $settings['client-id'] ) || empty( $settings['client-secret'] ) ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'skipped' => true,
|
||||
'message' => 'Missing Apilo credentials.',
|
||||
];
|
||||
}
|
||||
|
||||
$token = $this->apiloGetAccessToken( $refreshLeadSeconds );
|
||||
if ( !$token ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'skipped' => false,
|
||||
'message' => 'Unable to refresh Apilo token.',
|
||||
];
|
||||
}
|
||||
|
||||
$this->saveSetting( 'apilo', 'token-keepalive-at', date( 'Y-m-d H:i:s' ) );
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'skipped' => false,
|
||||
'message' => 'Apilo token keepalive OK.',
|
||||
];
|
||||
}
|
||||
|
||||
private function refreshApiloAccessToken( array $settings ): ?string
|
||||
{
|
||||
$postData = [
|
||||
'grantType' => 'refresh_token',
|
||||
'token' => $settings['refresh-token'],
|
||||
];
|
||||
|
||||
$ch = curl_init( "https://projectpro.apilo.com/rest/auth/token/" );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Basic " . base64_encode( $settings['client-id'] . ":" . $settings['client-secret'] ),
|
||||
"Accept: application/json"
|
||||
] );
|
||||
curl_setopt( $ch, CURLOPT_POST, true );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $postData ) );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
if ( curl_errno( $ch ) ) {
|
||||
curl_close( $ch );
|
||||
return null;
|
||||
}
|
||||
curl_close( $ch );
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
if ( empty( $response['accessToken'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->saveSetting( 'apilo', 'access-token', $response['accessToken'] );
|
||||
$this->saveSetting( 'apilo', 'refresh-token', $response['refreshToken'] ?? ( $settings['refresh-token'] ?? '' ) );
|
||||
$this->saveSetting( 'apilo', 'access-token-expire-at', $response['accessTokenExpireAt'] ?? null );
|
||||
$this->saveSetting( 'apilo', 'refresh-token-expire-at', $response['refreshTokenExpireAt'] ?? null );
|
||||
|
||||
return $response['accessToken'];
|
||||
}
|
||||
|
||||
private function shouldRefreshAccessToken( string $expiresAtRaw, int $leadSeconds = 300 ): bool
|
||||
{
|
||||
try {
|
||||
$expiresAt = new \DateTime( $expiresAtRaw );
|
||||
} catch ( \Exception $e ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$threshold = new \DateTime( date( 'Y-m-d H:i:s', time() + max( 0, $leadSeconds ) ) );
|
||||
return $expiresAt <= $threshold;
|
||||
}
|
||||
|
||||
private function isFutureDate( string $dateRaw ): bool
|
||||
{
|
||||
try {
|
||||
$date = new \DateTime( $dateRaw );
|
||||
} catch ( \Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $date > new \DateTime( date( 'Y-m-d H:i:s' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza aktualny stan integracji Apilo i zwraca komunikat dla UI.
|
||||
*
|
||||
* @return array{is_valid:bool,severity:string,message:string}
|
||||
*/
|
||||
public function apiloIntegrationStatus(): array
|
||||
{
|
||||
$settings = $this->getSettings( 'apilo' );
|
||||
|
||||
$missing = [];
|
||||
foreach ( [ 'client-id', 'client-secret' ] as $field ) {
|
||||
if ( trim( (string)($settings[$field] ?? '') ) === '' )
|
||||
$missing[] = $field;
|
||||
}
|
||||
|
||||
if ( !empty( $missing ) ) {
|
||||
return [
|
||||
'is_valid' => false,
|
||||
'severity' => 'danger',
|
||||
'message' => 'Brakuje konfiguracji Apilo: ' . implode( ', ', $missing ) . '.',
|
||||
];
|
||||
}
|
||||
|
||||
$accessToken = trim( (string)($settings['access-token'] ?? '') );
|
||||
$authorizationCode = trim( (string)($settings['authorization-code'] ?? '') );
|
||||
|
||||
if ( $accessToken === '' ) {
|
||||
if ( $authorizationCode === '' ) {
|
||||
return [
|
||||
'is_valid' => false,
|
||||
'severity' => 'warning',
|
||||
'message' => 'Brak authorization-code i access-token. Wpisz kod autoryzacji i uruchom autoryzacje.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'is_valid' => false,
|
||||
'severity' => 'warning',
|
||||
'message' => 'Brak access-token. Uruchom autoryzacje Apilo.',
|
||||
];
|
||||
}
|
||||
|
||||
$token = $this->apiloGetAccessToken();
|
||||
if ( !$token ) {
|
||||
return [
|
||||
'is_valid' => false,
|
||||
'severity' => 'danger',
|
||||
'message' => 'Token Apilo jest niewazny lub wygasl i nie udal sie refresh. Wykonaj ponowna autoryzacje.',
|
||||
];
|
||||
}
|
||||
|
||||
$expiresAt = trim( (string)($settings['access-token-expire-at'] ?? '') );
|
||||
$suffix = $expiresAt !== '' ? ( ' Token wazny do: ' . $expiresAt . '.' ) : '';
|
||||
|
||||
return [
|
||||
'is_valid' => true,
|
||||
'severity' => 'success',
|
||||
'message' => 'Integracja Apilo jest aktywna.' . $suffix,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Apilo API fetch lists ───────────────────────────────────
|
||||
|
||||
private const APILO_ENDPOINTS = [
|
||||
'platform' => 'https://projectpro.apilo.com/rest/api/orders/platform/map/',
|
||||
'status' => 'https://projectpro.apilo.com/rest/api/orders/status/map/',
|
||||
'carrier' => 'https://projectpro.apilo.com/rest/api/orders/carrier-account/map/',
|
||||
'payment' => 'https://projectpro.apilo.com/rest/api/orders/payment/map/',
|
||||
];
|
||||
|
||||
private const APILO_SETTINGS_KEYS = [
|
||||
'platform' => 'platform-list',
|
||||
'status' => 'status-types-list',
|
||||
'carrier' => 'carrier-account-list',
|
||||
'payment' => 'payment-types-list',
|
||||
];
|
||||
|
||||
/**
|
||||
* Fetch list from Apilo API and save to settings.
|
||||
* @param string $type platform|status|carrier|payment
|
||||
*/
|
||||
public function apiloFetchList( string $type ): bool
|
||||
{
|
||||
$result = $this->apiloFetchListResult( $type );
|
||||
return !empty( $result['success'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch list from Apilo API and return detailed status for UI.
|
||||
*
|
||||
* @param string $type platform|status|carrier|payment
|
||||
* @return array{success:bool,count:int,message:string}
|
||||
*/
|
||||
public function apiloFetchListResult( string $type ): array
|
||||
{
|
||||
if ( !isset( self::APILO_ENDPOINTS[$type] ) )
|
||||
throw new \InvalidArgumentException( "Unknown apilo list type: $type" );
|
||||
|
||||
$settings = $this->getSettings( 'apilo' );
|
||||
$missingFields = [];
|
||||
foreach ( [ 'client-id', 'client-secret' ] as $requiredField ) {
|
||||
if ( trim( (string)($settings[$requiredField] ?? '') ) === '' )
|
||||
$missingFields[] = $requiredField;
|
||||
}
|
||||
|
||||
if ( !empty( $missingFields ) ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Brakuje konfiguracji Apilo: ' . implode( ', ', $missingFields ) . '. Uzupelnij pola i zapisz ustawienia.',
|
||||
];
|
||||
}
|
||||
|
||||
$accessToken = $this->apiloGetAccessToken();
|
||||
if ( !$accessToken ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Brak aktywnego tokenu Apilo. Wykonaj autoryzacje Apilo i sprobuj ponownie.',
|
||||
];
|
||||
}
|
||||
|
||||
$ch = curl_init( self::APILO_ENDPOINTS[$type] );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $accessToken,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
if ( curl_errno( $ch ) ) {
|
||||
$error = curl_error( $ch );
|
||||
curl_close( $ch );
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Blad polaczenia z Apilo: ' . $error . '. Sprawdz polaczenie serwera i sprobuj ponownie.',
|
||||
];
|
||||
}
|
||||
$httpCode = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
$data = json_decode( $response, true );
|
||||
if ( !is_array( $data ) ) {
|
||||
$responsePreview = substr( trim( (string)$response ), 0, 180 );
|
||||
if ( $responsePreview === '' )
|
||||
$responsePreview = '[pusta odpowiedz]';
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Apilo zwrocilo niepoprawny format odpowiedzi (HTTP ' . $httpCode . '). Odpowiedz: ' . $responsePreview,
|
||||
];
|
||||
}
|
||||
|
||||
if ( $httpCode >= 400 ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Apilo zwrocilo blad HTTP ' . $httpCode . ': ' . $this->extractApiloErrorMessage( $data ),
|
||||
];
|
||||
}
|
||||
|
||||
$normalizedList = $this->normalizeApiloMapList( $data );
|
||||
if ( $normalizedList === null ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'count' => 0,
|
||||
'message' => 'Apilo zwrocilo dane w nieoczekiwanym formacie. Odswiez token i sproboj ponownie.',
|
||||
];
|
||||
}
|
||||
|
||||
$this->saveSetting( 'apilo', self::APILO_SETTINGS_KEYS[$type], $normalizedList );
|
||||
return [
|
||||
'success' => true,
|
||||
'count' => count( $normalizedList ),
|
||||
'message' => 'OK',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizuje odpowiedz API mapowania do listy rekordow ['id' => ..., 'name' => ...].
|
||||
* Zwraca null dla payloadu bledow lub nieoczekiwanego formatu.
|
||||
*
|
||||
* @return array<int, array{id:mixed,name:mixed}>|null
|
||||
*/
|
||||
private function normalizeApiloMapList( array $data ): ?array
|
||||
{
|
||||
if ( isset( $data['message'] ) && isset( $data['code'] ) )
|
||||
return null;
|
||||
|
||||
if ( $this->isMapListShape( $data ) )
|
||||
return $data;
|
||||
|
||||
if ( isset( $data['items'] ) && is_array( $data['items'] ) && $this->isMapListShape( $data['items'] ) )
|
||||
return $data['items'];
|
||||
|
||||
if ( isset( $data['data'] ) && is_array( $data['data'] ) && $this->isMapListShape( $data['data'] ) )
|
||||
return $data['data'];
|
||||
|
||||
// Dopuszczamy rowniez format asocjacyjny: [id => name, ...], ale tylko dla kluczy liczbowych.
|
||||
if ( !empty( $data ) ) {
|
||||
$normalized = [];
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( !( is_int( $key ) || ( is_string( $key ) && preg_match('/^-?\d+$/', $key) === 1 ) ) )
|
||||
return null;
|
||||
|
||||
if ( !is_scalar( $value ) )
|
||||
return null;
|
||||
|
||||
$normalized[] = [
|
||||
'id' => $key,
|
||||
'name' => (string) $value,
|
||||
];
|
||||
}
|
||||
|
||||
return !empty( $normalized ) ? $normalized : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isMapListShape( array $list ): bool
|
||||
{
|
||||
if ( empty( $list ) )
|
||||
return false;
|
||||
|
||||
foreach ( $list as $row ) {
|
||||
if ( !is_array( $row ) || !array_key_exists( 'id', $row ) || !array_key_exists( 'name', $row ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function extractApiloErrorMessage( array $data ): string
|
||||
{
|
||||
foreach ( [ 'message', 'error', 'detail', 'title' ] as $key ) {
|
||||
if ( isset( $data[$key] ) && is_scalar( $data[$key] ) ) {
|
||||
$message = trim( (string)$data[$key] );
|
||||
if ( $message !== '' )
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $data['errors'] ) ) {
|
||||
if ( is_array( $data['errors'] ) ) {
|
||||
$flat = [];
|
||||
foreach ( $data['errors'] as $errorItem ) {
|
||||
if ( is_scalar( $errorItem ) )
|
||||
$flat[] = (string)$errorItem;
|
||||
elseif ( is_array( $errorItem ) )
|
||||
$flat[] = json_encode( $errorItem, JSON_UNESCAPED_UNICODE );
|
||||
}
|
||||
|
||||
if ( !empty( $flat ) )
|
||||
return implode( '; ', $flat );
|
||||
} elseif ( is_scalar( $data['errors'] ) ) {
|
||||
return (string)$data['errors'];
|
||||
}
|
||||
}
|
||||
|
||||
return 'Nieznany blad odpowiedzi API.';
|
||||
}
|
||||
|
||||
// ── Apilo product operations ────────────────────────────────
|
||||
|
||||
public function getProductSku( int $productId ): ?string
|
||||
{
|
||||
$sku = $this->db->get( 'pp_shop_products', 'sku', [ 'id' => $productId ] );
|
||||
return $sku ?: null;
|
||||
}
|
||||
|
||||
public function apiloProductSearch( string $sku ): array
|
||||
{
|
||||
$accessToken = $this->apiloGetAccessToken();
|
||||
if ( !$accessToken )
|
||||
return [ 'status' => 'error', 'msg' => 'Brak tokenu Apilo.' ];
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/warehouse/product/?" . http_build_query( [ 'sku' => $sku ] );
|
||||
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $accessToken,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
if ( curl_errno( $ch ) ) {
|
||||
$error = curl_error( $ch );
|
||||
curl_close( $ch );
|
||||
return [ 'status' => 'error', 'msg' => 'Błąd cURL: ' . $error ];
|
||||
}
|
||||
curl_close( $ch );
|
||||
|
||||
$data = json_decode( $response, true );
|
||||
if ( $data && isset( $data['products'] ) ) {
|
||||
$data['status'] = 'SUCCESS';
|
||||
return $data;
|
||||
}
|
||||
|
||||
return [ 'status' => 'SUCCESS', 'msg' => 'Brak wyników dla podanego SKU.', 'products' => '' ];
|
||||
}
|
||||
|
||||
public function apiloCreateProduct( int $productId ): array
|
||||
{
|
||||
$accessToken = $this->apiloGetAccessToken();
|
||||
if ( !$accessToken )
|
||||
return [ 'success' => false, 'message' => 'Brak tokenu Apilo.' ];
|
||||
|
||||
$product = ( new \Domain\Product\ProductRepository( $this->db ) )->findCached( $productId );
|
||||
|
||||
$params = [
|
||||
'sku' => $product['sku'],
|
||||
'ean' => $product['ean'],
|
||||
'name' => $product['language']['name'],
|
||||
'tax' => (int) $product['vat'],
|
||||
'status' => 1,
|
||||
'quantity' => (int) $product['quantity'],
|
||||
'priceWithTax' => $product['price_brutto'],
|
||||
'description' => $product['language']['description'] . '<br>' . $product['language']['short_description'],
|
||||
'shortDescription' => '',
|
||||
'images' => [],
|
||||
];
|
||||
|
||||
foreach ( $product['images'] as $image )
|
||||
$params['images'][] = "https://" . $_SERVER['HTTP_HOST'] . $image['src'];
|
||||
|
||||
$ch = curl_init( "https://projectpro.apilo.com/rest/api/warehouse/product/" );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [ $params ] ) );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $accessToken,
|
||||
"Content-Type: application/json",
|
||||
"Accept: application/json"
|
||||
] );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) ) {
|
||||
$error = curl_error( $ch );
|
||||
curl_close( $ch );
|
||||
return [ 'success' => false, 'message' => 'Błąd cURL: ' . $error ];
|
||||
}
|
||||
curl_close( $ch );
|
||||
|
||||
if ( !empty( $responseData['products'] ) ) {
|
||||
$this->db->update( 'pp_shop_products', [
|
||||
'apilo_product_id' => reset( $responseData['products'] ),
|
||||
'apilo_product_name' => $product['language']['name'],
|
||||
], [ 'id' => $product['id'] ] );
|
||||
|
||||
return [ 'success' => true, 'message' => 'Produkt został dodany do magazynu APILO.' ];
|
||||
}
|
||||
|
||||
return [ 'success' => false, 'message' => 'Podczas dodawania produktu wystąpił błąd.' ];
|
||||
}
|
||||
|
||||
// ── ShopPRO import ──────────────────────────────────────────
|
||||
|
||||
public function shopproImportProduct( int $productId ): array
|
||||
{
|
||||
$settings = $this->getSettings( 'shoppro' );
|
||||
|
||||
$mdb2 = new \medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $settings['db_name'],
|
||||
'server' => $settings['db_host'],
|
||||
'username' => $settings['db_user'],
|
||||
'password' => $settings['db_password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$product = $mdb2->get( 'pp_shop_products', '*', [ 'id' => $productId ] );
|
||||
if ( !$product )
|
||||
return [ 'success' => false, 'message' => 'Podczas importowania produktu wystąpił błąd.' ];
|
||||
|
||||
$this->db->insert( 'pp_shop_products', [
|
||||
'price_netto' => $product['price_netto'],
|
||||
'price_brutto' => $product['price_brutto'],
|
||||
'vat' => $product['vat'],
|
||||
'stock_0_buy' => $product['stock_0_buy'],
|
||||
'quantity' => $product['quantity'],
|
||||
'wp' => $product['wp'],
|
||||
'sku' => $product['sku'],
|
||||
'ean' => $product['ean'],
|
||||
'custom_label_0' => $product['custom_label_0'],
|
||||
'custom_label_1' => $product['custom_label_1'],
|
||||
'custom_label_2' => $product['custom_label_2'],
|
||||
'custom_label_3' => $product['custom_label_3'],
|
||||
'custom_label_4' => $product['custom_label_4'],
|
||||
'additional_message' => $product['additional_message'],
|
||||
'additional_message_text' => $product['additional_message_text'],
|
||||
'additional_message_required'=> $product['additional_message_required'],
|
||||
'weight' => $product['weight'],
|
||||
] );
|
||||
|
||||
$newProductId = $this->db->id();
|
||||
if ( !$newProductId )
|
||||
return [ 'success' => false, 'message' => 'Podczas importowania produktu wystąpił błąd.' ];
|
||||
|
||||
// Import translations
|
||||
$languages = $mdb2->select( 'pp_shop_products_langs', '*', [ 'product_id' => $productId ] );
|
||||
if ( is_array( $languages ) ) {
|
||||
foreach ( $languages as $lang ) {
|
||||
$this->db->insert( 'pp_shop_products_langs', [
|
||||
'product_id' => $newProductId,
|
||||
'lang_id' => $lang['lang_id'],
|
||||
'name' => $lang['name'],
|
||||
'short_description' => $lang['short_description'],
|
||||
'description' => $lang['description'],
|
||||
'tab_name_1' => $lang['tab_name_1'],
|
||||
'tab_description_1' => $lang['tab_description_1'],
|
||||
'tab_name_2' => $lang['tab_name_2'],
|
||||
'tab_description_2' => $lang['tab_description_2'],
|
||||
'meta_title' => $lang['meta_title'],
|
||||
'meta_description' => $lang['meta_description'],
|
||||
'meta_keywords' => $lang['meta_keywords'],
|
||||
'seo_link' => $lang['seo_link'],
|
||||
'copy_from' => $lang['copy_from'],
|
||||
'warehouse_message_zero' => $lang['warehouse_message_zero'],
|
||||
'warehouse_message_nonzero'=> $lang['warehouse_message_nonzero'],
|
||||
'canonical' => $lang['canonical'],
|
||||
'xml_name' => $lang['xml_name'],
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
// Import images
|
||||
$images = $mdb2->select( 'pp_shop_products_images', '*', [ 'product_id' => $productId ] );
|
||||
if ( is_array( $images ) ) {
|
||||
foreach ( $images as $image ) {
|
||||
$imageUrl = 'https://' . $settings['domain'] . $image['src'];
|
||||
|
||||
$ch = curl_init( $imageUrl );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
|
||||
$imageData = curl_exec( $ch );
|
||||
curl_close( $ch );
|
||||
|
||||
$imageName = basename( $imageUrl );
|
||||
$imageDir = '../upload/product_images/product_' . $newProductId;
|
||||
$imagePath = $imageDir . '/' . $imageName;
|
||||
|
||||
if ( !file_exists( $imageDir ) )
|
||||
mkdir( $imageDir, 0777, true );
|
||||
|
||||
file_put_contents( $imagePath, $imageData );
|
||||
|
||||
$this->db->insert( 'pp_shop_products_images', [
|
||||
'product_id' => $newProductId,
|
||||
'src' => '/upload/product_images/product_' . $newProductId . '/' . $imageName,
|
||||
'o' => $image['o'],
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
return [ 'success' => true, 'message' => 'Produkt został zaimportowany.' ];
|
||||
}
|
||||
}
|
||||
429
autoload/Domain/Languages/LanguagesRepository.php
Normal file
429
autoload/Domain/Languages/LanguagesRepository.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
namespace Domain\Languages;
|
||||
|
||||
class LanguagesRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'o',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'pl.id',
|
||||
'name' => 'pl.name',
|
||||
'status' => 'pl.status',
|
||||
'start' => 'pl.start',
|
||||
'o' => 'pl.o',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'pl.o';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'pl.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'pl.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$start = trim((string)($filters['start'] ?? ''));
|
||||
if ($start === '0' || $start === '1') {
|
||||
$where[] = 'pl.start = :start';
|
||||
$params[':start'] = (int)$start;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_langs AS pl
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
pl.id,
|
||||
pl.name,
|
||||
pl.status,
|
||||
pl.start,
|
||||
pl.o
|
||||
FROM pp_langs AS pl
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, pl.o ASC, pl.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listTranslationsForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'text',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'plt.id',
|
||||
'text' => 'plt.text',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'plt.text';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$text = trim((string)($filters['text'] ?? ''));
|
||||
if ($text !== '') {
|
||||
if (strlen($text) > 255) {
|
||||
$text = substr($text, 0, 255);
|
||||
}
|
||||
$where[] = 'plt.text LIKE :text';
|
||||
$params[':text'] = '%' . $text . '%';
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_langs_translations AS plt
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
plt.id,
|
||||
plt.text
|
||||
FROM pp_langs_translations AS plt
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, plt.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function languageDetails(string $languageId): ?array
|
||||
{
|
||||
$language = $this->db->get('pp_langs', '*', ['id' => $languageId]);
|
||||
return $language ?: null;
|
||||
}
|
||||
|
||||
public function translationDetails(int $translationId): ?array
|
||||
{
|
||||
$translation = $this->db->get('pp_langs_translations', '*', ['id' => $translationId]);
|
||||
return $translation ?: null;
|
||||
}
|
||||
|
||||
public function maxOrder(): int
|
||||
{
|
||||
$max = $this->db->max('pp_langs', 'o');
|
||||
return $max ? (int)$max : 0;
|
||||
}
|
||||
|
||||
public function languagesList(bool $onlyActive = false): array
|
||||
{
|
||||
$where = [];
|
||||
if ($onlyActive) {
|
||||
$where['status'] = 1;
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_langs', '*', array_merge(['ORDER' => ['o' => 'ASC']], $where));
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public function defaultLanguageId(): string
|
||||
{
|
||||
$languages = $this->languagesList();
|
||||
if (empty($languages)) {
|
||||
return 'pl';
|
||||
}
|
||||
|
||||
foreach ($languages as $language) {
|
||||
if ((int)($language['start'] ?? 0) === 1 && !empty($language['id'])) {
|
||||
return (string)$language['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($languages[0]['id'])) {
|
||||
return (string)$languages[0]['id'];
|
||||
}
|
||||
|
||||
return 'pl';
|
||||
}
|
||||
|
||||
public function deleteLanguage(string $languageId): bool
|
||||
{
|
||||
$languageId = $this->sanitizeLanguageId($languageId);
|
||||
if ($languageId === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_langs') <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->db->count('pp_langs', ['id' => $languageId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dropResult = $this->db->query('ALTER TABLE pp_langs_translations DROP COLUMN `' . $languageId . '`');
|
||||
if (!$dropResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$deleteResult = $this->db->delete('pp_langs', ['id' => $languageId]);
|
||||
if (!$deleteResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function saveLanguage(string $languageId, string $name, $status, $start, int $order): ?string
|
||||
{
|
||||
$languageId = $this->sanitizeLanguageId($languageId);
|
||||
if ($languageId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$statusVal = $this->toSwitchValue($status);
|
||||
$startVal = $this->toSwitchValue($start);
|
||||
|
||||
$exists = (bool)$this->db->count('pp_langs', ['id' => $languageId]);
|
||||
|
||||
if ($startVal === 1) {
|
||||
$this->db->update('pp_langs', ['start' => 0], ['id[!]' => $languageId]);
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
$this->db->update('pp_langs', [
|
||||
'status' => $statusVal,
|
||||
'start' => $startVal,
|
||||
'name' => $name,
|
||||
'o' => $order,
|
||||
], [
|
||||
'id' => $languageId,
|
||||
]);
|
||||
} else {
|
||||
$addResult = $this->db->query('ALTER TABLE pp_langs_translations ADD COLUMN `' . $languageId . '` TEXT NULL DEFAULT NULL');
|
||||
if (!$addResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$insertResult = $this->db->insert('pp_langs', [
|
||||
'id' => $languageId,
|
||||
'name' => $name,
|
||||
'status' => $statusVal,
|
||||
'start' => $startVal,
|
||||
'o' => $order,
|
||||
]);
|
||||
if (!$insertResult) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(int)$this->db->count('pp_langs', ['start' => 1])) {
|
||||
$idTmp = (string)$this->db->get('pp_langs', 'id', ['ORDER' => ['o' => 'ASC']]);
|
||||
if ($idTmp !== '') {
|
||||
$this->db->update('pp_langs', ['start' => 1], ['id' => $idTmp]);
|
||||
}
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return $languageId;
|
||||
}
|
||||
|
||||
public function deleteTranslation(int $translationId): bool
|
||||
{
|
||||
$result = $this->db->delete('pp_langs_translations', ['id' => $translationId]);
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
public function saveTranslation(int $translationId, string $text, array $translations): ?int
|
||||
{
|
||||
if ($translationId > 0) {
|
||||
$this->db->update('pp_langs_translations', ['text' => $text], ['id' => $translationId]);
|
||||
} else {
|
||||
$insertResult = $this->db->insert('pp_langs_translations', ['text' => $text]);
|
||||
if (!$insertResult) {
|
||||
return null;
|
||||
}
|
||||
$translationId = (int)$this->db->id();
|
||||
}
|
||||
|
||||
if ($translationId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($translations as $languageId => $value) {
|
||||
$safeLanguageId = $this->sanitizeLanguageId((string)$languageId);
|
||||
if ($safeLanguageId === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->update('pp_langs_translations', [
|
||||
$safeLanguageId => (string)$value,
|
||||
], [
|
||||
'id' => $translationId,
|
||||
]);
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return $translationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca ID domyslnego jezyka (z flaga start=1) z cache Redis.
|
||||
*/
|
||||
public function defaultLanguage(): string
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'Domain\Languages\LanguagesRepository::defaultLanguage';
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$stmt = $this->db->query(
|
||||
'SELECT id FROM pp_langs WHERE status = 1 ORDER BY start DESC, o ASC LIMIT 1'
|
||||
);
|
||||
$results = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
$defaultLanguage = $results[0][0] ?? 'pl';
|
||||
|
||||
$cacheHandler->set($cacheKey, $defaultLanguage);
|
||||
|
||||
return $defaultLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca liste aktywnych jezykow z cache Redis.
|
||||
*/
|
||||
public function activeLanguages(): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'Domain\Languages\LanguagesRepository::activeLanguages';
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$activeLanguages = $this->db->select(
|
||||
'pp_langs',
|
||||
['id', 'name'],
|
||||
['status' => 1, 'ORDER' => ['o' => 'ASC']]
|
||||
);
|
||||
|
||||
if (!is_array($activeLanguages)) {
|
||||
$activeLanguages = [];
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $activeLanguages);
|
||||
|
||||
return $activeLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca tlumaczenia dla danego jezyka z cache Redis.
|
||||
*/
|
||||
public function translations(string $language = 'pl'): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "Domain\Languages\LanguagesRepository::translations:$language";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$translations = ['0' => $language];
|
||||
|
||||
$results = $this->db->select('pp_langs_translations', ['text', $language]);
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$translations[$row['text']] = $row[$language];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $translations);
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
private function sanitizeLanguageId(string $languageId): ?string
|
||||
{
|
||||
$languageId = strtolower(trim($languageId));
|
||||
if (!preg_match('/^[a-z]{2}$/', $languageId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $languageId;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
return ($value === 'on' || $value === 1 || $value === '1' || $value === true) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
540
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
540
autoload/Domain/Layouts/LayoutsRepository.php
Normal file
@@ -0,0 +1,540 @@
|
||||
<?php
|
||||
namespace Domain\Layouts;
|
||||
|
||||
class LayoutsRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function delete(int $layoutId): bool
|
||||
{
|
||||
if ((int)$this->db->count('pp_layouts') <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$deleted = (bool)$this->db->delete('pp_layouts', ['id' => $layoutId]);
|
||||
if ($deleted) {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
$this->clearFrontLayoutsCache();
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
public function find(int $layoutId): array
|
||||
{
|
||||
$layout = $this->db->get('pp_layouts', '*', ['id' => $layoutId]);
|
||||
if (!is_array($layout)) {
|
||||
return $this->defaultLayout();
|
||||
}
|
||||
|
||||
$layout['pages'] = $this->db->select('pp_layouts_pages', 'page_id', ['layout_id' => $layoutId]);
|
||||
$layout['categories'] = $this->db->select('pp_layouts_categories', 'category_id', ['layout_id' => $layoutId]);
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$layoutId = (int)($data['id'] ?? 0);
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$categoriesDefault = $this->toSwitchValue($data['categories_default'] ?? 0);
|
||||
|
||||
$row = [
|
||||
'name' => (string)($data['name'] ?? ''),
|
||||
'html' => (string)($data['html'] ?? ''),
|
||||
'css' => (string)($data['css'] ?? ''),
|
||||
'js' => (string)($data['js'] ?? ''),
|
||||
'status' => $status,
|
||||
'categories_default' => $categoriesDefault,
|
||||
];
|
||||
|
||||
if ($status === 1) {
|
||||
$this->db->update('pp_layouts', ['status' => 0]);
|
||||
}
|
||||
|
||||
if ($categoriesDefault === 1) {
|
||||
$this->db->update('pp_layouts', ['categories_default' => 0]);
|
||||
}
|
||||
|
||||
if ($layoutId <= 0) {
|
||||
$this->db->insert('pp_layouts', $row);
|
||||
$layoutId = (int)$this->db->id();
|
||||
if ($layoutId <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_layouts', $row, ['id' => $layoutId]);
|
||||
}
|
||||
|
||||
$this->db->delete('pp_layouts_pages', ['layout_id' => $layoutId]);
|
||||
$this->syncPages($layoutId, $data['pages'] ?? []);
|
||||
|
||||
$this->db->delete('pp_layouts_categories', ['layout_id' => $layoutId]);
|
||||
$this->syncCategories($layoutId, $data['categories'] ?? []);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
$this->clearFrontLayoutsCache();
|
||||
|
||||
return $layoutId;
|
||||
}
|
||||
|
||||
public function listAll(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_layouts', '*', ['ORDER' => ['name' => 'ASC']]);
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public function menusWithPages(): array
|
||||
{
|
||||
$menus = $this->db->select('pp_menus', '*', ['ORDER' => ['id' => 'ASC']]);
|
||||
if (!is_array($menus)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($menus as $key => $menu) {
|
||||
$menuId = (int)($menu['id'] ?? 0);
|
||||
$menus[$key]['pages'] = $this->menuPages($menuId, null);
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
public function categoriesTree($parentId = null): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_categories', ['id'], [
|
||||
'parent_id' => $parentId,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach ($rows as $row) {
|
||||
$categoryId = (int)($row['id'] ?? 0);
|
||||
if ($categoryId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $categoryId]);
|
||||
if (!is_array($category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations = $this->db->select('pp_shop_categories_langs', '*', ['category_id' => $categoryId]);
|
||||
$category['languages'] = [];
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $translation) {
|
||||
$langId = (string)($translation['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$category['languages'][$langId] = $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$category['subcategories'] = $this->categoriesTree($categoryId);
|
||||
$categories[] = $category;
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'pl.id',
|
||||
'name' => 'pl.name',
|
||||
'status' => 'pl.status',
|
||||
'categories_default' => 'pl.categories_default',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'pl.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'pl.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'pl.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$categoriesDefault = trim((string)($filters['categories_default'] ?? ''));
|
||||
if ($categoriesDefault === '0' || $categoriesDefault === '1') {
|
||||
$where[] = 'pl.categories_default = :categories_default';
|
||||
$params[':categories_default'] = (int)$categoriesDefault;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_layouts AS pl
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
pl.id,
|
||||
pl.name,
|
||||
pl.status,
|
||||
pl.categories_default
|
||||
FROM pp_layouts AS pl
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, pl.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Frontend methods ──────────────────────────────────────────
|
||||
|
||||
public function categoryDefaultLayoutId()
|
||||
{
|
||||
return $this->db->get('pp_layouts', 'id', ['categories_default' => 1]);
|
||||
}
|
||||
|
||||
public function getDefaultLayout(): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'LayoutsRepository::getDefaultLayout';
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached) && !empty($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$layout = $this->db->get('pp_layouts', '*', ['status' => 1]);
|
||||
if (!is_array($layout) || empty($layout)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $layout);
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function getProductLayout(int $productId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "LayoutsRepository::getProductLayout:$productId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached) && !empty($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$stmt = $this->db->query(
|
||||
"SELECT pp_layouts.*
|
||||
FROM pp_layouts
|
||||
JOIN pp_shop_products ON pp_layouts.id = pp_shop_products.layout_id
|
||||
WHERE pp_shop_products.id = " . (int)$productId . "
|
||||
ORDER BY pp_layouts.id DESC"
|
||||
);
|
||||
$layoutRows = $stmt ? $stmt->fetchAll(\PDO::FETCH_ASSOC) : [];
|
||||
|
||||
if (is_array($layoutRows) && isset($layoutRows[0])) {
|
||||
$layout = $layoutRows[0];
|
||||
} else {
|
||||
$stmt2 = $this->db->query(
|
||||
"SELECT pp_layouts.*
|
||||
FROM pp_layouts
|
||||
JOIN pp_layouts_categories ON pp_layouts.id = pp_layouts_categories.layout_id
|
||||
JOIN pp_shop_products_categories ON pp_shop_products_categories.category_id = pp_layouts_categories.category_id
|
||||
WHERE pp_shop_products_categories.product_id = " . (int)$productId . "
|
||||
ORDER BY pp_shop_products_categories.o ASC, pp_layouts.id DESC"
|
||||
);
|
||||
$layoutRows = $stmt2 ? $stmt2->fetchAll(\PDO::FETCH_ASSOC) : [];
|
||||
|
||||
if (is_array($layoutRows) && isset($layoutRows[0])) {
|
||||
$layout = $layoutRows[0];
|
||||
} else {
|
||||
$layout = $this->db->get('pp_layouts', '*', ['categories_default' => 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$layout) {
|
||||
$layout = $this->db->get('pp_layouts', '*', ['status' => 1]);
|
||||
}
|
||||
|
||||
if (!is_array($layout) || empty($layout)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $layout);
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function getArticleLayout(int $articleId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "LayoutsRepository::getArticleLayout:$articleId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$layout = $this->db->get('pp_layouts', ['[><]pp_articles' => ['id' => 'layout_id']], '*', ['pp_articles.id' => (int)$articleId]);
|
||||
|
||||
if (is_array($layout)) {
|
||||
$cacheHandler->set($cacheKey, $layout);
|
||||
return $layout;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getCategoryLayout(int $categoryId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "LayoutsRepository::getCategoryLayout:$categoryId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached) && !empty($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$stmt = $this->db->query(
|
||||
"SELECT pp_layouts.*
|
||||
FROM pp_layouts
|
||||
JOIN pp_layouts_categories ON pp_layouts.id = pp_layouts_categories.layout_id
|
||||
WHERE pp_layouts_categories.category_id = " . (int)$categoryId . "
|
||||
ORDER BY pp_layouts.id DESC"
|
||||
);
|
||||
$layoutRows = $stmt ? $stmt->fetchAll(\PDO::FETCH_ASSOC) : [];
|
||||
|
||||
if (is_array($layoutRows) && isset($layoutRows[0])) {
|
||||
$layout = $layoutRows[0];
|
||||
} else {
|
||||
$layout = $this->db->get('pp_layouts', '*', ['categories_default' => 1]);
|
||||
}
|
||||
|
||||
if (!$layout) {
|
||||
$layout = $this->db->get('pp_layouts', '*', ['status' => 1]);
|
||||
}
|
||||
|
||||
if (!is_array($layout) || empty($layout)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $layout);
|
||||
return $layout;
|
||||
}
|
||||
|
||||
public function getActiveLayout(int $pageId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "LayoutsRepository::getActiveLayout:$pageId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$layout = $this->db->get('pp_layouts', ['[><]pp_layouts_pages' => ['id' => 'layout_id']], '*', ['page_id' => (int)$pageId]);
|
||||
|
||||
if (!$layout) {
|
||||
$layout = $this->db->get('pp_layouts', '*', ['status' => 1]);
|
||||
}
|
||||
|
||||
if (is_array($layout)) {
|
||||
$cacheHandler->set($cacheKey, $layout);
|
||||
return $layout;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Private helpers ──────────────────────────────────────────
|
||||
|
||||
private function syncPages(int $layoutId, $pages): void
|
||||
{
|
||||
foreach ($this->normalizeIds($pages) as $pageId) {
|
||||
$this->db->delete('pp_layouts_pages', ['page_id' => $pageId]);
|
||||
$this->db->insert('pp_layouts_pages', [
|
||||
'layout_id' => $layoutId,
|
||||
'page_id' => $pageId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function syncCategories(int $layoutId, $categories): void
|
||||
{
|
||||
foreach ($this->normalizeIds($categories) as $categoryId) {
|
||||
$this->db->delete('pp_layouts_categories', ['category_id' => $categoryId]);
|
||||
$this->db->insert('pp_layouts_categories', [
|
||||
'layout_id' => $layoutId,
|
||||
'category_id' => $categoryId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function normalizeIds($values): array
|
||||
{
|
||||
if (!is_array($values)) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($values as $value) {
|
||||
$id = (int)$value;
|
||||
if ($id > 0) {
|
||||
$ids[$id] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($ids);
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
return ($value === 'on' || $value === 1 || $value === '1' || $value === true) ? 1 : 0;
|
||||
}
|
||||
|
||||
private function defaultLayout(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 0,
|
||||
'categories_default' => 0,
|
||||
'html' => '',
|
||||
'css' => '',
|
||||
'js' => '',
|
||||
'pages' => [],
|
||||
'categories' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function clearFrontLayoutsCache(): void
|
||||
{
|
||||
if (!class_exists('\Shared\Cache\CacheHandler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
if (method_exists($cacheHandler, 'deletePattern')) {
|
||||
$cacheHandler->deletePattern('*Layouts::*');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Inwalidacja cache nie moze blokowac zapisu/usuwania.
|
||||
}
|
||||
}
|
||||
|
||||
private function menuPages(int $menuId, $parentId = null): array
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages', ['id', 'menu_id', 'status', 'parent_id', 'start'], [
|
||||
'AND' => [
|
||||
'menu_id' => $menuId,
|
||||
'parent_id' => $parentId,
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pages = [];
|
||||
foreach ($rows as $row) {
|
||||
$pageId = (int)($row['id'] ?? 0);
|
||||
if ($pageId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row['title'] = $this->pageTitle($pageId);
|
||||
$row['subpages'] = $this->menuPages($menuId, $pageId);
|
||||
$pages[] = $row;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
private function pageTitle(int $pageId): string
|
||||
{
|
||||
$result = $this->db->select('pp_pages_langs', [
|
||||
'[><]pp_langs' => ['lang_id' => 'id'],
|
||||
], 'title', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'title[!]' => '',
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
'LIMIT' => 1,
|
||||
]);
|
||||
|
||||
if (is_array($result) && isset($result[0])) {
|
||||
return (string)$result[0];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
59
autoload/Domain/Newsletter/NewsletterPreviewRenderer.php
Normal file
59
autoload/Domain/Newsletter/NewsletterPreviewRenderer.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Domain\Newsletter;
|
||||
|
||||
class NewsletterPreviewRenderer
|
||||
{
|
||||
public function render(array $articles, array $settings, ?array $template, string $dates = ''): string
|
||||
{
|
||||
$out = '<div style="border-bottom: 1px solid #ccc;">';
|
||||
$out .= !empty($settings['newsletter_header'])
|
||||
? (string)$settings['newsletter_header']
|
||||
: '<p style="text-align: center;">--- brak zdefiniowanego naglowka ---</p>';
|
||||
$out .= '</div>';
|
||||
|
||||
$out .= '<div style="border-bottom: 1px solid #ccc; padding: 10px 0 0 0;">';
|
||||
|
||||
if (is_array($template) && !empty($template)) {
|
||||
$out .= '<div style="padding: 10px; background: #F1F1F1; margin-bottom: 10px">';
|
||||
$out .= (string)($template['text'] ?? '');
|
||||
$out .= '</div>';
|
||||
}
|
||||
|
||||
if (is_array($articles) && !empty($articles)) {
|
||||
foreach ($articles as $article) {
|
||||
$articleId = (int)($article['id'] ?? 0);
|
||||
$title = (string)($article['language']['title'] ?? '');
|
||||
$seoLink = trim((string)($article['language']['seo_link'] ?? ''));
|
||||
$url = $seoLink !== '' ? $seoLink : ('a-' . $articleId . '-' . \Shared\Helpers\Helpers::seo($title));
|
||||
$entry = !empty($article['language']['entry'])
|
||||
? (string)$article['language']['entry']
|
||||
: (string)($article['language']['text'] ?? '');
|
||||
|
||||
$out .= '<div style="padding: 10px; background: #F1F1F1; margin-bottom: 10px">';
|
||||
$out .= '<a href="http://' . htmlspecialchars((string)($_SERVER['SERVER_NAME'] ?? ''), ENT_QUOTES, 'UTF-8') . '/'
|
||||
. htmlspecialchars($url, ENT_QUOTES, 'UTF-8')
|
||||
. '" title="' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8')
|
||||
. '" style="margin-bottom: 10px; display: block; font-size: 14px; color: #5b7fb1; font-weight: 600;">'
|
||||
. htmlspecialchars($title, ENT_QUOTES, 'UTF-8')
|
||||
. '</a>';
|
||||
$out .= '<div>' . $entry . '</div>';
|
||||
$out .= '<div style="clear: both;"></div>';
|
||||
$out .= '</div>';
|
||||
}
|
||||
} elseif (trim($dates) !== '') {
|
||||
$out .= '<div style="padding: 10px; background: #F1F1F1; margin-bottom: 10px; text-align: center;">';
|
||||
$out .= '--- brak artykulow w danym okresie ---';
|
||||
$out .= '</div>';
|
||||
}
|
||||
|
||||
$out .= '</div>';
|
||||
|
||||
$out .= '<div style="border-bottom: 1px solid #ccc; padding: 10px 0 0 0;">';
|
||||
$out .= !empty($settings['newsletter_footer'])
|
||||
? (string)$settings['newsletter_footer']
|
||||
: '<p style="text-align: center;">--- brak zdefiniowanej stopki ---</p>';
|
||||
$out .= '</div>';
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
445
autoload/Domain/Newsletter/NewsletterRepository.php
Normal file
445
autoload/Domain/Newsletter/NewsletterRepository.php
Normal file
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
namespace Domain\Newsletter;
|
||||
|
||||
use Domain\Settings\SettingsRepository;
|
||||
use Domain\Article\ArticleRepository;
|
||||
|
||||
class NewsletterRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
private SettingsRepository $settingsRepository;
|
||||
private ?ArticleRepository $articleRepository;
|
||||
private ?NewsletterPreviewRenderer $previewRenderer;
|
||||
|
||||
public function __construct(
|
||||
$db,
|
||||
?SettingsRepository $settingsRepository = null,
|
||||
?ArticleRepository $articleRepository = null,
|
||||
?NewsletterPreviewRenderer $previewRenderer = null
|
||||
) {
|
||||
$this->db = $db;
|
||||
$this->settingsRepository = $settingsRepository ?? new SettingsRepository($db);
|
||||
$this->articleRepository = $articleRepository;
|
||||
$this->previewRenderer = $previewRenderer;
|
||||
}
|
||||
|
||||
private function getArticleRepository(): ArticleRepository
|
||||
{
|
||||
return $this->articleRepository ?? ($this->articleRepository = new ArticleRepository($this->db));
|
||||
}
|
||||
|
||||
private function getPreviewRenderer(): NewsletterPreviewRenderer
|
||||
{
|
||||
return $this->previewRenderer ?? ($this->previewRenderer = new NewsletterPreviewRenderer());
|
||||
}
|
||||
|
||||
public function getSettings(): array
|
||||
{
|
||||
return $this->settingsRepository->getSettings();
|
||||
}
|
||||
|
||||
public function saveSettings(array $values): bool
|
||||
{
|
||||
$this->settingsRepository->updateSetting('newsletter_footer', (string)($values['newsletter_footer'] ?? ''));
|
||||
$this->settingsRepository->updateSetting('newsletter_header', (string)($values['newsletter_header'] ?? ''));
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function queueSend(string $dates = '', int $templateId = 0): bool
|
||||
{
|
||||
$subscribers = $this->db->select('pp_newsletter', 'email', ['status' => 1]);
|
||||
if (!is_array($subscribers) || empty($subscribers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$cleanDates = trim($dates);
|
||||
$templateId = $templateId > 0 ? $templateId : 0;
|
||||
|
||||
foreach ($subscribers as $subscriber) {
|
||||
$email = is_array($subscriber) ? (string)($subscriber['email'] ?? '') : (string)$subscriber;
|
||||
if ($email === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_newsletter_send', [
|
||||
'email' => $email,
|
||||
'dates' => $cleanDates,
|
||||
'id_template' => $templateId > 0 ? $templateId : null,
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function templateByName(string $templateName): string
|
||||
{
|
||||
return (string)$this->db->get('pp_newsletter_templates', 'text', ['name' => $templateName]);
|
||||
}
|
||||
|
||||
public function templateDetails(int $templateId): ?array
|
||||
{
|
||||
if ($templateId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = $this->db->get('pp_newsletter_templates', '*', ['id' => $templateId]);
|
||||
if (!is_array($row)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function isAdminTemplate(int $templateId): bool
|
||||
{
|
||||
$isAdmin = $this->db->get('pp_newsletter_templates', 'is_admin', ['id' => $templateId]);
|
||||
return (int)$isAdmin === 1;
|
||||
}
|
||||
|
||||
public function deleteTemplate(int $templateId): bool
|
||||
{
|
||||
if ($templateId <= 0 || $this->isAdminTemplate($templateId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_newsletter_templates', ['id' => $templateId]);
|
||||
}
|
||||
|
||||
public function saveTemplate(int $templateId, string $name, string $text): ?int
|
||||
{
|
||||
$templateId = max(0, $templateId);
|
||||
$name = trim($name);
|
||||
|
||||
if ($templateId <= 0) {
|
||||
if ($name === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$ok = $this->db->insert('pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text,
|
||||
'is_admin' => 0,
|
||||
]);
|
||||
if (!$ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return (int)$this->db->id();
|
||||
}
|
||||
|
||||
$current = $this->templateDetails($templateId);
|
||||
if (!is_array($current)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((int)($current['is_admin'] ?? 0) === 1) {
|
||||
$name = (string)($current['name'] ?? '');
|
||||
}
|
||||
|
||||
$this->db->update('pp_newsletter_templates', [
|
||||
'name' => $name,
|
||||
'text' => $text,
|
||||
], [
|
||||
'id' => $templateId,
|
||||
]);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return $templateId;
|
||||
}
|
||||
|
||||
public function listTemplatesSimple(bool $adminTemplates = false): array
|
||||
{
|
||||
$rows = $this->db->select('pp_newsletter_templates', '*', [
|
||||
'is_admin' => $adminTemplates ? 1 : 0,
|
||||
'ORDER' => ['name' => 'ASC'],
|
||||
]);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public function deleteSubscriber(int $subscriberId): bool
|
||||
{
|
||||
if ($subscriberId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_newsletter', ['id' => $subscriberId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listSubscribersForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'email',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'pn.id',
|
||||
'email' => 'pn.email',
|
||||
'status' => 'pn.status',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'pn.email';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$email = trim((string)($filters['email'] ?? ''));
|
||||
if ($email !== '') {
|
||||
if (strlen($email) > 255) {
|
||||
$email = substr($email, 0, 255);
|
||||
}
|
||||
$where[] = 'pn.email LIKE :email';
|
||||
$params[':email'] = '%' . $email . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'pn.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_newsletter AS pn
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
pn.id,
|
||||
pn.email,
|
||||
pn.status
|
||||
FROM pp_newsletter AS pn
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, pn.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listTemplatesForAdmin(
|
||||
bool $adminTemplates,
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'pnt.id',
|
||||
'name' => 'pnt.name',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'pnt.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['pnt.is_admin = :is_admin'];
|
||||
$params = [':is_admin' => $adminTemplates ? 1 : 0];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'pnt.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_newsletter_templates AS pnt
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
pnt.id,
|
||||
pnt.name,
|
||||
pnt.is_admin
|
||||
FROM pp_newsletter_templates AS pnt
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, pnt.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
// ── Frontend methods ──
|
||||
|
||||
public function unsubscribe(string $hash): bool
|
||||
{
|
||||
$id = $this->db->get('pp_newsletter', 'id', ['hash' => $hash]);
|
||||
if (!$id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->delete('pp_newsletter', ['id' => $id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function confirmSubscription(string $hash): bool
|
||||
{
|
||||
$id = $this->db->get('pp_newsletter', 'id', ['AND' => ['hash' => $hash, 'status' => 0]]);
|
||||
if (!$id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->update('pp_newsletter', ['status' => 1], ['id' => $id]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getHashByEmail(string $email): ?string
|
||||
{
|
||||
$hash = $this->db->get('pp_newsletter', 'hash', ['email' => $email]);
|
||||
return $hash ? (string)$hash : null;
|
||||
}
|
||||
|
||||
public function removeByEmail(string $email): bool
|
||||
{
|
||||
if (!$this->db->get('pp_newsletter', 'id', ['email' => $email])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_newsletter', ['email' => $email]);
|
||||
}
|
||||
|
||||
public function signup(string $email, string $serverName, bool $ssl, array $settings): bool
|
||||
{
|
||||
if (!\Shared\Helpers\Helpers::email_check($email)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->db->get('pp_newsletter', 'id', ['email' => $email])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hash = md5(time() . $email);
|
||||
|
||||
$text = ($settings['newsletter_header'] ?? '');
|
||||
$text .= $this->templateByName('#potwierdzenie-zapisu-do-newslettera');
|
||||
$text .= ($settings['newsletter_footer'] ?? '');
|
||||
|
||||
$base = $ssl ? 'https' : 'http';
|
||||
$link = '/newsletter/confirm/hash=' . $hash;
|
||||
$text = str_replace('[LINK]', $link, $text);
|
||||
$text = str_replace('[WYPISZ_SIE]', '', $text);
|
||||
|
||||
$text = preg_replace(
|
||||
"-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i",
|
||||
"$1" . $base . "://" . $serverName . "$2$4",
|
||||
$text
|
||||
);
|
||||
$text = preg_replace(
|
||||
"-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i",
|
||||
"$1" . $base . "://" . $serverName . "$2$4",
|
||||
$text
|
||||
);
|
||||
|
||||
$lang = \Shared\Helpers\Helpers::get_session('lang-' . \Shared\Helpers\Helpers::get_session('current-lang'));
|
||||
$subject = $lang['potwierdz-zapisanie-sie-do-newslettera'] ?? 'Newsletter';
|
||||
\Shared\Helpers\Helpers::send_email($email, $subject, $text);
|
||||
|
||||
$this->db->insert('pp_newsletter', ['email' => $email, 'hash' => $hash, 'status' => 0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendQueued(int $limit, string $serverName, bool $ssl, string $unsubscribeLabel): bool
|
||||
{
|
||||
$settingsDetails = $this->settingsRepository->getSettings();
|
||||
|
||||
$results = $this->db->query('SELECT * FROM pp_newsletter_send ORDER BY id ASC LIMIT ' . (int)$limit);
|
||||
$results = $results ? $results->fetchAll() : [];
|
||||
|
||||
if (!is_array($results) || empty($results)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$renderer = $this->getPreviewRenderer();
|
||||
$articleRepo = $this->getArticleRepository();
|
||||
|
||||
foreach ($results as $row) {
|
||||
$dates = explode(' - ', $row['dates']);
|
||||
|
||||
$articles = [];
|
||||
if (isset($dates[0], $dates[1])) {
|
||||
$articles = $articleRepo->articlesByDateAdd($dates[0], $dates[1]);
|
||||
}
|
||||
|
||||
$text = $renderer->render(
|
||||
is_array($articles) ? $articles : [],
|
||||
$settingsDetails,
|
||||
$this->templateDetails((int)$row['id_template']),
|
||||
(string)$row['dates']
|
||||
);
|
||||
|
||||
$base = $ssl ? 'https' : 'http';
|
||||
|
||||
$text = preg_replace(
|
||||
"-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i",
|
||||
"$1" . $base . "://" . $serverName . "$2$4",
|
||||
$text
|
||||
);
|
||||
$text = preg_replace(
|
||||
"-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|http://).)*)(['\"][^>]*>)-i",
|
||||
"$1" . $base . "://" . $serverName . "$2$4",
|
||||
$text
|
||||
);
|
||||
|
||||
$hash = $this->getHashByEmail($row['email']);
|
||||
$link = $base . "://" . $serverName . '/newsletter/unsubscribe/hash=' . $hash;
|
||||
$text = str_replace('[WYPISZ_SIE]', '<a href="' . $link . '">' . $unsubscribeLabel . '</a>', $text);
|
||||
|
||||
\Shared\Helpers\Helpers::send_email($row['email'], 'Newsletter ze strony: ' . $serverName, $text);
|
||||
|
||||
$this->db->delete('pp_newsletter_send', ['id' => $row['id']]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
803
autoload/Domain/Order/OrderAdminService.php
Normal file
803
autoload/Domain/Order/OrderAdminService.php
Normal file
@@ -0,0 +1,803 @@
|
||||
<?php
|
||||
namespace Domain\Order;
|
||||
|
||||
class OrderAdminService
|
||||
{
|
||||
private OrderRepository $orders;
|
||||
private $productRepo;
|
||||
private $settingsRepo;
|
||||
private $transportRepo;
|
||||
|
||||
public function __construct(
|
||||
OrderRepository $orders,
|
||||
$productRepo = null,
|
||||
$settingsRepo = null,
|
||||
$transportRepo = null
|
||||
) {
|
||||
$this->orders = $orders;
|
||||
$this->productRepo = $productRepo;
|
||||
$this->settingsRepo = $settingsRepo;
|
||||
$this->transportRepo = $transportRepo;
|
||||
}
|
||||
|
||||
public function details(int $orderId): array
|
||||
{
|
||||
return $this->orders->findForAdmin($orderId);
|
||||
}
|
||||
|
||||
public function statuses(): array
|
||||
{
|
||||
return $this->orders->orderStatuses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'date_order',
|
||||
string $sortDir = 'DESC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
return $this->orders->listForAdmin($filters, $sortColumn, $sortDir, $page, $perPage);
|
||||
}
|
||||
|
||||
public function nextOrderId(int $orderId): ?int
|
||||
{
|
||||
return $this->orders->nextOrderId($orderId);
|
||||
}
|
||||
|
||||
public function prevOrderId(int $orderId): ?int
|
||||
{
|
||||
return $this->orders->prevOrderId($orderId);
|
||||
}
|
||||
|
||||
public function saveNotes(int $orderId, string $notes): bool
|
||||
{
|
||||
return $this->orders->saveNotes($orderId, $notes);
|
||||
}
|
||||
|
||||
public function saveOrderByAdmin(array $input): bool
|
||||
{
|
||||
$saved = $this->orders->saveOrderByAdmin(
|
||||
(int)($input['order_id'] ?? 0),
|
||||
(string)($input['client_name'] ?? ''),
|
||||
(string)($input['client_surname'] ?? ''),
|
||||
(string)($input['client_street'] ?? ''),
|
||||
(string)($input['client_postal_code'] ?? ''),
|
||||
(string)($input['client_city'] ?? ''),
|
||||
(string)($input['client_email'] ?? ''),
|
||||
(string)($input['firm_name'] ?? ''),
|
||||
(string)($input['firm_street'] ?? ''),
|
||||
(string)($input['firm_postal_code'] ?? ''),
|
||||
(string)($input['firm_city'] ?? ''),
|
||||
(string)($input['firm_nip'] ?? ''),
|
||||
(int)($input['transport_id'] ?? 0),
|
||||
(string)($input['inpost_paczkomat'] ?? ''),
|
||||
(int)($input['payment_method_id'] ?? 0)
|
||||
);
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Order products management (admin)
|
||||
// =========================================================================
|
||||
|
||||
public function searchProducts(string $query, string $langId): array
|
||||
{
|
||||
if (!$this->productRepo || trim($query) === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->productRepo->searchProductByNameAjax($query, $langId);
|
||||
$results = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$productId = (int)($row['product_id'] ?? 0);
|
||||
if ($productId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$product = $this->productRepo->findCached($productId, $langId);
|
||||
if (!is_array($product)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = isset($product['language']['name']) ? (string)$product['language']['name'] : '';
|
||||
$img = $this->productRepo->getProductImg($productId);
|
||||
|
||||
$results[] = [
|
||||
'product_id' => $productId,
|
||||
'parent_product_id' => (int)($product['parent_id'] ?? 0),
|
||||
'name' => $name,
|
||||
'sku' => (string)($product['sku'] ?? ''),
|
||||
'ean' => (string)($product['ean'] ?? ''),
|
||||
'price_brutto' => (float)($product['price_brutto'] ?? 0),
|
||||
'price_brutto_promo' => (float)($product['price_brutto_promo'] ?? 0),
|
||||
'vat' => (float)($product['vat'] ?? 0),
|
||||
'quantity' => (int)($product['quantity'] ?? 0),
|
||||
'image' => $img,
|
||||
];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function saveOrderProducts(int $orderId, array $productsData): bool
|
||||
{
|
||||
if ($orderId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currentProducts = $this->orders->orderProducts($orderId);
|
||||
$currentById = [];
|
||||
foreach ($currentProducts as $cp) {
|
||||
$currentById[(int)$cp['id']] = $cp;
|
||||
}
|
||||
|
||||
$submittedIds = [];
|
||||
|
||||
foreach ($productsData as $item) {
|
||||
$orderProductId = (int)($item['order_product_id'] ?? 0);
|
||||
$deleted = !empty($item['delete']);
|
||||
|
||||
if ($deleted && $orderProductId > 0) {
|
||||
// Usunięcie — zwrot na stan
|
||||
$existing = isset($currentById[$orderProductId]) ? $currentById[$orderProductId] : null;
|
||||
if ($existing) {
|
||||
$this->adjustStock((int)$existing['product_id'], (int)$existing['quantity']);
|
||||
}
|
||||
$this->orders->deleteOrderProduct($orderProductId);
|
||||
$submittedIds[] = $orderProductId;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($orderProductId > 0 && isset($currentById[$orderProductId])) {
|
||||
// Istniejący produkt — aktualizacja
|
||||
$existing = $currentById[$orderProductId];
|
||||
$newQty = max(1, (int)($item['quantity'] ?? 1));
|
||||
$oldQty = (int)$existing['quantity'];
|
||||
$qtyDiff = $oldQty - $newQty;
|
||||
|
||||
$update = [
|
||||
'quantity' => $newQty,
|
||||
'price_brutto' => (float)($item['price_brutto'] ?? $existing['price_brutto']),
|
||||
'price_brutto_promo' => (float)($item['price_brutto_promo'] ?? $existing['price_brutto_promo']),
|
||||
];
|
||||
|
||||
$this->orders->updateOrderProduct($orderProductId, $update);
|
||||
|
||||
// Korekta stanu: qtyDiff > 0 = zmniejszono ilość = zwrot na stan
|
||||
if ($qtyDiff !== 0) {
|
||||
$this->adjustStock((int)$existing['product_id'], $qtyDiff);
|
||||
}
|
||||
|
||||
$submittedIds[] = $orderProductId;
|
||||
} elseif ($orderProductId === 0) {
|
||||
// Nowy produkt
|
||||
$productId = (int)($item['product_id'] ?? 0);
|
||||
$qty = max(1, (int)($item['quantity'] ?? 1));
|
||||
|
||||
$this->orders->addOrderProduct($orderId, [
|
||||
'product_id' => $productId,
|
||||
'parent_product_id' => (int)($item['parent_product_id'] ?? $productId),
|
||||
'name' => (string)($item['name'] ?? ''),
|
||||
'attributes' => '',
|
||||
'vat' => (float)($item['vat'] ?? 0),
|
||||
'price_brutto' => (float)($item['price_brutto'] ?? 0),
|
||||
'price_brutto_promo' => (float)($item['price_brutto_promo'] ?? 0),
|
||||
'quantity' => $qty,
|
||||
'message' => '',
|
||||
'custom_fields' => '',
|
||||
]);
|
||||
|
||||
// Zmniejsz stan magazynowy
|
||||
$this->adjustStock($productId, -$qty);
|
||||
}
|
||||
}
|
||||
|
||||
// Usunięte z formularza (nie przesłane) — zwrot na stan
|
||||
foreach ($currentById as $cpId => $cp) {
|
||||
if (!in_array($cpId, $submittedIds)) {
|
||||
$this->adjustStock((int)$cp['product_id'], (int)$cp['quantity']);
|
||||
$this->orders->deleteOrderProduct($cpId);
|
||||
}
|
||||
}
|
||||
|
||||
// Przelicz koszt dostawy (próg darmowej dostawy)
|
||||
$this->recalculateTransportCost($orderId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getFreeDeliveryThreshold(): float
|
||||
{
|
||||
if (!$this->settingsRepo) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (float)$this->settingsRepo->getSingleValue('free_delivery');
|
||||
}
|
||||
|
||||
private function adjustStock(int $productId, int $delta): void
|
||||
{
|
||||
if (!$this->productRepo || $productId <= 0 || $delta === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentQty = $this->productRepo->getQuantity($productId);
|
||||
if ($currentQty === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newQty = max(0, $currentQty + $delta);
|
||||
$this->productRepo->updateQuantity($productId, $newQty);
|
||||
}
|
||||
|
||||
private function recalculateTransportCost(int $orderId): void
|
||||
{
|
||||
$order = $this->orders->findRawById($orderId);
|
||||
if (!$order) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transportId = (int)($order['transport_id'] ?? 0);
|
||||
if ($transportId <= 0 || !$this->transportRepo || !$this->settingsRepo) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transport = $this->transportRepo->findActiveById($transportId);
|
||||
if (!is_array($transport)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Oblicz sumę produktów (bez dostawy)
|
||||
$productsSummary = $this->calculateProductsTotal($orderId);
|
||||
$freeDelivery = (float)$this->settingsRepo->getSingleValue('free_delivery');
|
||||
|
||||
if ((int)($transport['delivery_free'] ?? 0) === 1 && $freeDelivery > 0 && $productsSummary >= $freeDelivery) {
|
||||
$transportCost = 0.0;
|
||||
} else {
|
||||
$transportCost = (float)($transport['cost'] ?? 0);
|
||||
}
|
||||
|
||||
$this->orders->updateTransportCost($orderId, $transportCost);
|
||||
}
|
||||
|
||||
private function calculateProductsTotal(int $orderId): float
|
||||
{
|
||||
$products = $this->orders->orderProducts($orderId);
|
||||
$summary = 0.0;
|
||||
|
||||
foreach ($products as $row) {
|
||||
$pricePromo = (float)($row['price_brutto_promo'] ?? 0);
|
||||
$price = (float)($row['price_brutto'] ?? 0);
|
||||
$quantity = (float)($row['quantity'] ?? 0);
|
||||
|
||||
if ($pricePromo > 0) {
|
||||
$summary += $pricePromo * $quantity;
|
||||
} else {
|
||||
$summary += $price * $quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
public function changeStatus(int $orderId, int $status, bool $sendEmail): array
|
||||
{
|
||||
$order = $this->orders->findRawById($orderId);
|
||||
if (!$order || (int)$order['status'] === $status) {
|
||||
return ['result' => false];
|
||||
}
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
|
||||
if ($this->orders->updateOrderStatus($orderId, $status))
|
||||
{
|
||||
$this->orders->insertStatusHistory($orderId, $status, $sendEmail ? 1 : 0);
|
||||
|
||||
$response = ['result' => true];
|
||||
|
||||
if ($sendEmail)
|
||||
{
|
||||
$order['status'] = $status;
|
||||
$response['email'] = $this->sendStatusChangeEmail($order);
|
||||
}
|
||||
|
||||
// Apilo status sync
|
||||
$this->syncApiloStatusIfNeeded($order, $status);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return ['result' => false];
|
||||
}
|
||||
|
||||
public function resendConfirmationEmail(int $orderId): bool
|
||||
{
|
||||
global $settings;
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$order = $this->orders->orderDetailsFrontend($orderId);
|
||||
if (!$order || !$order['id']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$coupon = (int)$order['coupon_id'] ? (new \Domain\Coupon\CouponRepository($db))->find((int)$order['coupon_id']) : null;
|
||||
|
||||
$mail_order = \Shared\Tpl\Tpl::view('shop-order/mail-summary', [
|
||||
'settings' => $settings,
|
||||
'order' => $order,
|
||||
'coupon' => $coupon,
|
||||
]);
|
||||
|
||||
$settings['ssl'] ? $base = 'https' : $base = 'http';
|
||||
|
||||
$regex = "-(<img[^>]+src\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order);
|
||||
|
||||
$regex = "-(<a[^>]+href\s*=\s*['\"])(((?!'|\"|https?://).)*)(['\"][^>]*>)-i";
|
||||
$mail_order = preg_replace($regex, "$1" . $base . "://" . $_SERVER['SERVER_NAME'] . "$2$4", $mail_order);
|
||||
|
||||
\Shared\Helpers\Helpers::send_email($order['client_email'], \Shared\Helpers\Helpers::lang('potwierdzenie-zamowienia-ze-sklepu') . ' ' . $settings['firm_name'], $mail_order);
|
||||
\Shared\Helpers\Helpers::send_email($settings['contact_email'], 'Nowe zamówienie / ' . $settings['firm_name'] . ' / ' . $order['number'] . ' - ' . $order['client_surname'] . ' ' . $order['client_name'], $mail_order);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setOrderAsUnpaid(int $orderId): bool
|
||||
{
|
||||
$this->orders->setAsUnpaid($orderId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setOrderAsPaid(int $orderId, bool $sendMail): bool
|
||||
{
|
||||
$order = $this->orders->findRawById($orderId);
|
||||
if (!$order) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apilo payment sync
|
||||
$this->syncApiloPaymentIfNeeded($order);
|
||||
|
||||
// Mark as paid
|
||||
$this->orders->setAsPaid($orderId);
|
||||
|
||||
// Set status to 1 (opłacone) without email
|
||||
$this->changeStatus($orderId, 1, false);
|
||||
|
||||
// Set status to 4 (przyjęte do realizacji) with email
|
||||
$this->changeStatus($orderId, 4, $sendMail);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendOrderToApilo(int $orderId): bool
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ($orderId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$order = $this->orders->findForAdmin($orderId);
|
||||
if (empty($order) || empty($order['apilo_order_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository( $mdb );
|
||||
$accessToken = $integrationsRepository -> apiloGetAccessToken();
|
||||
if (!$accessToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$newStatus = 8;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, 'https://projectpro.apilo.com/rest/api/orders/' . $order['apilo_order_id'] . '/status/');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'id' => (int)$order['apilo_order_id'],
|
||||
'status' => (int)( new \Domain\ShopStatus\ShopStatusRepository($mdb) )->getApiloStatusId( (int)$newStatus ),
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/json',
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$apiloResultRaw = curl_exec($ch);
|
||||
$apiloResult = json_decode((string)$apiloResultRaw, true);
|
||||
|
||||
if (!is_array($apiloResult) || (int)($apiloResult['updates'] ?? 0) !== 1) {
|
||||
curl_close($ch);
|
||||
return false;
|
||||
}
|
||||
|
||||
$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_orders' AND COLUMN_NAME != 'id'";
|
||||
$stmt = $mdb->query($query);
|
||||
$columns = $stmt ? $stmt->fetchAll(\PDO::FETCH_COLUMN) : [];
|
||||
$columnsList = implode(', ', $columns);
|
||||
$mdb->query('INSERT INTO pp_shop_orders (' . $columnsList . ') SELECT ' . $columnsList . ' FROM pp_shop_orders pso WHERE pso.id = ' . $orderId);
|
||||
$newOrderId = (int)$mdb->id();
|
||||
|
||||
$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_order_products' AND COLUMN_NAME != 'id' AND COLUMN_NAME != 'order_id'";
|
||||
$stmt2 = $mdb->query($query);
|
||||
$columns = $stmt2 ? $stmt2->fetchAll(\PDO::FETCH_COLUMN) : [];
|
||||
$columnsList = implode(', ', $columns);
|
||||
$mdb->query('INSERT INTO pp_shop_order_products (order_id, ' . $columnsList . ') SELECT ' . $newOrderId . ', ' . $columnsList . ' FROM pp_shop_order_products psop WHERE psop.order_id = ' . $orderId);
|
||||
|
||||
$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'pp_shop_order_statuses' AND COLUMN_NAME != 'id' AND COLUMN_NAME != 'order_id'";
|
||||
$stmt3 = $mdb->query($query);
|
||||
$columns = $stmt3 ? $stmt3->fetchAll(\PDO::FETCH_COLUMN) : [];
|
||||
$columnsList = implode(', ', $columns);
|
||||
$mdb->query('INSERT INTO pp_shop_order_statuses (order_id, ' . $columnsList . ') SELECT ' . $newOrderId . ', ' . $columnsList . ' FROM pp_shop_order_statuses psos WHERE psos.order_id = ' . $orderId);
|
||||
|
||||
$mdb->delete('pp_shop_orders', ['id' => $orderId]);
|
||||
$mdb->delete('pp_shop_order_products', ['order_id' => $orderId]);
|
||||
$mdb->delete('pp_shop_order_statuses', ['order_id' => $orderId]);
|
||||
|
||||
$mdb->update('pp_shop_orders', ['apilo_order_id' => null], ['id' => $newOrderId]);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function toggleTrustmateSend(int $orderId): array
|
||||
{
|
||||
$newValue = $this->orders->toggleTrustmateSend($orderId);
|
||||
if ($newValue === null) {
|
||||
return [
|
||||
'result' => false,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'result' => true,
|
||||
'trustmate_send' => $newValue,
|
||||
];
|
||||
}
|
||||
|
||||
public function deleteOrder(int $orderId): bool
|
||||
{
|
||||
return $this->orders->deleteOrder($orderId);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Apilo sync queue (migrated from \shop\Order)
|
||||
// =========================================================================
|
||||
|
||||
private const APILO_SYNC_QUEUE_FILE = '/temp/apilo-sync-queue.json';
|
||||
|
||||
public function processApiloSyncQueue(int $limit = 10): int
|
||||
{
|
||||
$queue = self::loadApiloSyncQueue();
|
||||
if (!\Shared\Helpers\Helpers::is_array_fix($queue)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$processed = 0;
|
||||
|
||||
foreach ($queue as $key => $task)
|
||||
{
|
||||
if ($processed >= $limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
$order_id = (int)($task['order_id'] ?? 0);
|
||||
if ($order_id <= 0) {
|
||||
unset($queue[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$order = $this->orders->findRawById($order_id);
|
||||
if (!$order) {
|
||||
unset($queue[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$sync_failed = false;
|
||||
|
||||
$payment_pending = !empty($task['payment']) && (int)$order['paid'] === 1;
|
||||
if ($payment_pending && (int)$order['apilo_order_id']) {
|
||||
if (!$this->syncApiloPayment($order)) {
|
||||
$sync_failed = true;
|
||||
$error = 'payment_sync_failed';
|
||||
}
|
||||
}
|
||||
|
||||
$status_pending = isset($task['status']) && $task['status'] !== null && $task['status'] !== '';
|
||||
if (!$sync_failed && $status_pending && (int)$order['apilo_order_id']) {
|
||||
if (!$this->syncApiloStatus($order, (int)$task['status'])) {
|
||||
$sync_failed = true;
|
||||
$error = 'status_sync_failed';
|
||||
}
|
||||
}
|
||||
|
||||
if ($sync_failed) {
|
||||
$task['attempts'] = (int)($task['attempts'] ?? 0) + 1;
|
||||
$task['last_error'] = $error;
|
||||
$task['updated_at'] = date('Y-m-d H:i:s');
|
||||
$queue[$key] = $task;
|
||||
} else {
|
||||
unset($queue[$key]);
|
||||
}
|
||||
|
||||
$processed++;
|
||||
}
|
||||
|
||||
self::saveApiloSyncQueue($queue);
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Private: email
|
||||
// =========================================================================
|
||||
|
||||
private function sendStatusChangeEmail(array $order): bool
|
||||
{
|
||||
if (!$order['client_email']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$order_statuses = $this->orders->orderStatuses();
|
||||
$firm_name = (new \Domain\Settings\SettingsRepository($db))->getSingleValue('firm_name');
|
||||
|
||||
$status = (int)$order['status'];
|
||||
$number = $order['number'];
|
||||
|
||||
$subjects = [
|
||||
0 => $firm_name . ' - zamówienie [NUMER] zostało złożone',
|
||||
1 => $firm_name . ' - zamówienie [NUMER] zostało opłacone',
|
||||
2 => $firm_name . ' - płatność za zamówienie [NUMER] została odrzucona',
|
||||
3 => $firm_name . ' - płatność za zamówienie [NUMER] jest sprawdzania ręcznie',
|
||||
4 => $firm_name . ' - zamówienie [NUMER] zostało przyjęte do realizacji',
|
||||
5 => $firm_name . ' - zamówienie [NUMER] zostało wysłane',
|
||||
6 => $firm_name . ' - zamówienie [NUMER] zostało zrealizowane',
|
||||
7 => $firm_name . ' - zamówienie [NUMER] zostało przygotowane go wysłania',
|
||||
8 => $firm_name . ' - zamówienie [NUMER] zostało anulowane',
|
||||
];
|
||||
|
||||
$subject = isset($subjects[$status]) ? str_replace('[NUMER]', $number, $subjects[$status]) : '';
|
||||
if (!$subject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$email = new \Shared\Email\Email();
|
||||
$email->load_by_name('#sklep-zmiana-statusu-zamowienia');
|
||||
|
||||
$email->text = str_replace('[NUMER_ZAMOWIENIA]', $number, $email->text);
|
||||
$email->text = str_replace('[DATA_ZAMOWIENIA]', date('Y/m/d', strtotime($order['date_order'])), $email->text);
|
||||
$email->text = str_replace('[STATUS]', isset($order_statuses[$status]) ? $order_statuses[$status] : '', $email->text);
|
||||
|
||||
return $email->send($order['client_email'], $subject, true);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Private: Apilo sync
|
||||
// =========================================================================
|
||||
|
||||
private function syncApiloPaymentIfNeeded(array $order): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
|
||||
$apilo_settings = $integrationsRepository->getSettings('apilo');
|
||||
|
||||
if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
|
||||
self::appendApiloLog("SET AS PAID\n" . print_r($order, true));
|
||||
}
|
||||
|
||||
if ($order['apilo_order_id'] && !$this->syncApiloPayment($order)) {
|
||||
self::queueApiloSync((int)$order['id'], true, null, 'payment_sync_failed');
|
||||
}
|
||||
}
|
||||
|
||||
private function syncApiloStatusIfNeeded(array $order, int $status): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
|
||||
$apilo_settings = $integrationsRepository->getSettings('apilo');
|
||||
|
||||
if (!$apilo_settings['enabled'] || !$apilo_settings['access-token'] || !$apilo_settings['sync_orders']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
|
||||
self::appendApiloLog("UPDATE STATUS\n" . print_r($order, true));
|
||||
}
|
||||
|
||||
if ($order['apilo_order_id'] && !$this->syncApiloStatus($order, $status)) {
|
||||
self::queueApiloSync((int)$order['id'], false, $status, 'status_sync_failed');
|
||||
}
|
||||
}
|
||||
|
||||
private function syncApiloPayment(array $order): bool
|
||||
{
|
||||
global $config;
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
|
||||
|
||||
if (!(int)$order['apilo_order_id']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$payment_type = (int)(new \Domain\PaymentMethod\PaymentMethodRepository($db))->getApiloPaymentTypeId((int)$order['payment_method_id']);
|
||||
if ($payment_type <= 0) {
|
||||
$payment_type = 1;
|
||||
}
|
||||
|
||||
$payment_date = new \DateTime($order['date_order']);
|
||||
$access_token = $integrationsRepository->apiloGetAccessToken();
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/payment/');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'amount' => str_replace(',', '.', $order['summary']),
|
||||
'paymentDate' => $payment_date->format('Y-m-d\TH:i:s\Z'),
|
||||
'type' => $payment_type,
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json",
|
||||
"Content-Type: application/json",
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
$apilo_response = curl_exec($ch);
|
||||
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_errno($ch) ? curl_error($ch) : '';
|
||||
curl_close($ch);
|
||||
|
||||
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
|
||||
self::appendApiloLog("PAYMENT RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_response, true));
|
||||
}
|
||||
|
||||
if ($curl_error !== '') return false;
|
||||
if ($http_code < 200 || $http_code >= 300) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function syncApiloStatus(array $order, int $status): bool
|
||||
{
|
||||
global $config;
|
||||
|
||||
$db = $this->orders->getDb();
|
||||
$integrationsRepository = new \Domain\Integrations\IntegrationsRepository($db);
|
||||
|
||||
if (!(int)$order['apilo_order_id']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$access_token = $integrationsRepository->apiloGetAccessToken();
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "https://projectpro.apilo.com/rest/api/orders/" . $order['apilo_order_id'] . '/status/');
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'id' => $order['apilo_order_id'],
|
||||
'status' => (int)(new \Domain\ShopStatus\ShopStatusRepository($db))->getApiloStatusId($status),
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json",
|
||||
"Content-Type: application/json",
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||
$apilo_result = curl_exec($ch);
|
||||
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_errno($ch) ? curl_error($ch) : '';
|
||||
curl_close($ch);
|
||||
|
||||
if (isset($config['debug']['apilo']) && $config['debug']['apilo']) {
|
||||
self::appendApiloLog("STATUS RESPONSE\nHTTP: " . $http_code . "\nCURL: " . $curl_error . "\n" . print_r($apilo_result, true));
|
||||
}
|
||||
|
||||
if ($curl_error !== '') return false;
|
||||
if ($http_code < 200 || $http_code >= 300) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Private: Apilo sync queue file helpers
|
||||
// =========================================================================
|
||||
|
||||
private static function queueApiloSync(int $order_id, bool $payment, ?int $status, string $error): void
|
||||
{
|
||||
if ($order_id <= 0) return;
|
||||
|
||||
$queue = self::loadApiloSyncQueue();
|
||||
$key = (string)$order_id;
|
||||
$row = is_array($queue[$key] ?? null) ? $queue[$key] : [];
|
||||
|
||||
$row['order_id'] = $order_id;
|
||||
$row['payment'] = !empty($row['payment']) || $payment ? 1 : 0;
|
||||
if ($status !== null) {
|
||||
$row['status'] = $status;
|
||||
}
|
||||
|
||||
$row['attempts'] = (int)($row['attempts'] ?? 0) + 1;
|
||||
$row['last_error'] = $error;
|
||||
$row['updated_at'] = date('Y-m-d H:i:s');
|
||||
|
||||
$queue[$key] = $row;
|
||||
self::saveApiloSyncQueue($queue);
|
||||
}
|
||||
|
||||
private static function apiloSyncQueuePath(): string
|
||||
{
|
||||
return dirname(__DIR__, 2) . self::APILO_SYNC_QUEUE_FILE;
|
||||
}
|
||||
|
||||
private static function loadApiloSyncQueue(): array
|
||||
{
|
||||
$path = self::apiloSyncQueuePath();
|
||||
if (!file_exists($path)) return [];
|
||||
|
||||
$content = file_get_contents($path);
|
||||
if (!$content) return [];
|
||||
|
||||
$decoded = json_decode($content, true);
|
||||
if (!is_array($decoded)) return [];
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
private static function saveApiloSyncQueue(array $queue): void
|
||||
{
|
||||
$path = self::apiloSyncQueuePath();
|
||||
$dir = dirname($path);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($path, json_encode($queue, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
|
||||
}
|
||||
|
||||
private static function appendApiloLog(string $message): void
|
||||
{
|
||||
$base = isset($_SERVER['DOCUMENT_ROOT']) && $_SERVER['DOCUMENT_ROOT']
|
||||
? rtrim($_SERVER['DOCUMENT_ROOT'], '/\\')
|
||||
: dirname(__DIR__, 2);
|
||||
|
||||
$dir = $base . '/logs';
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$dir . '/apilo.txt',
|
||||
date('Y-m-d H:i:s') . ' --- ' . $message . "\n\n",
|
||||
FILE_APPEND
|
||||
);
|
||||
}
|
||||
}
|
||||
1045
autoload/Domain/Order/OrderRepository.php
Normal file
1045
autoload/Domain/Order/OrderRepository.php
Normal file
File diff suppressed because it is too large
Load Diff
785
autoload/Domain/Pages/PagesRepository.php
Normal file
785
autoload/Domain/Pages/PagesRepository.php
Normal file
@@ -0,0 +1,785 @@
|
||||
<?php
|
||||
namespace Domain\Pages;
|
||||
|
||||
class PagesRepository
|
||||
{
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const PAGE_TYPES = [
|
||||
0 => 'pelne artykuly',
|
||||
1 => 'wprowadzenia',
|
||||
2 => 'miniaturki',
|
||||
3 => 'link',
|
||||
4 => 'kontakt',
|
||||
5 => 'kategoria sklepu',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const SORT_TYPES = [
|
||||
0 => 'data dodania - najstarsze na poczatku',
|
||||
1 => 'data dodania - najnowsze na poczatku',
|
||||
2 => 'data modyfikacji - rosnaco',
|
||||
3 => 'data modyfikacji - malejaco',
|
||||
4 => 'reczne',
|
||||
5 => 'alfabetycznie - A - Z',
|
||||
6 => 'alfabetycznie - Z - A',
|
||||
];
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function pageTypes(): array
|
||||
{
|
||||
return self::PAGE_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function sortTypes(): array
|
||||
{
|
||||
return self::SORT_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menusList(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_menus', '*', ['ORDER' => ['id' => 'ASC']]);
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menusWithPages(): array
|
||||
{
|
||||
$menus = $this->menusList();
|
||||
foreach ($menus as $index => $menu) {
|
||||
$menuId = (int)($menu['id'] ?? 0);
|
||||
$menus[$index]['pages'] = $this->menuPages($menuId);
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menuPages(int $menuId, ?int $parentId = null): array
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages', ['id', 'menu_id', 'status', 'parent_id', 'start'], [
|
||||
'AND' => [
|
||||
'menu_id' => $menuId,
|
||||
'parent_id' => $parentId,
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pages = [];
|
||||
foreach ($rows as $row) {
|
||||
$pageId = (int)($row['id'] ?? 0);
|
||||
if ($pageId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row['title'] = $this->pageTitle($pageId);
|
||||
$row['languages'] = $this->pageLanguages($pageId);
|
||||
$row['subpages'] = $this->menuPages($menuId, $pageId);
|
||||
$pages[] = $row;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
public function menuDelete(int $menuId): bool
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_pages', ['menu_id' => $menuId]) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_menus', ['id' => $menuId]);
|
||||
}
|
||||
|
||||
public function pageDelete(int $pageId): bool
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_pages', ['parent_id' => $pageId]) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_pages', ['id' => $pageId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function menuDetails(int $menuId): array
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$menu = $this->db->get('pp_menus', '*', ['id' => $menuId]);
|
||||
if (!is_array($menu)) {
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function menuSave(int $menuId, string $name, $status): bool
|
||||
{
|
||||
$statusValue = $this->toSwitchValue($status);
|
||||
|
||||
if ($menuId <= 0) {
|
||||
$result = $this->db->insert('pp_menus', [
|
||||
'name' => $name,
|
||||
'status' => $statusValue,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
}
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
$this->db->update('pp_menus', [
|
||||
'name' => $name,
|
||||
'status' => $statusValue,
|
||||
], [
|
||||
'id' => $menuId,
|
||||
]);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function pageDetails(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return $this->defaultPage();
|
||||
}
|
||||
|
||||
$page = $this->db->get('pp_pages', '*', ['id' => $pageId]);
|
||||
if (!is_array($page)) {
|
||||
return $this->defaultPage();
|
||||
}
|
||||
|
||||
$translations = $this->db->select('pp_pages_langs', '*', ['page_id' => $pageId]);
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $row) {
|
||||
$langId = (string)($row['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$page['languages'][$langId] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$page['layout_id'] = (int)$this->db->get('pp_layouts_pages', 'layout_id', ['page_id' => $pageId]);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function pageArticles(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
ap.article_id,
|
||||
ap.o,
|
||||
a.status,
|
||||
(
|
||||
SELECT title
|
||||
FROM pp_articles_langs AS pal
|
||||
JOIN pp_langs AS pl ON pal.lang_id = pl.id
|
||||
WHERE pal.article_id = ap.article_id
|
||||
AND pal.title != ""
|
||||
ORDER BY pl.o ASC
|
||||
LIMIT 1
|
||||
) AS title
|
||||
FROM pp_articles_pages AS ap
|
||||
JOIN pp_articles AS a ON a.id = ap.article_id
|
||||
WHERE ap.page_id = :page_id
|
||||
AND a.status != -1
|
||||
ORDER BY ap.o ASC
|
||||
';
|
||||
|
||||
$stmt = $this->db->query($sql, [':page_id' => $pageId]);
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public function saveArticlesOrder(int $pageId, $articles): bool
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($articles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->db->update('pp_articles_pages', ['o' => 0], ['page_id' => $pageId]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($articles as $item) {
|
||||
$articleId = (int)($item['item_id'] ?? 0);
|
||||
if ($articleId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$this->db->update('pp_articles_pages', ['o' => $position], [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'article_id' => $articleId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function savePagesOrder(int $menuId, $pages): bool
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($pages)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->db->update('pp_pages', ['o' => 0], ['menu_id' => $menuId]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($pages as $item) {
|
||||
$itemId = (int)($item['item_id'] ?? 0);
|
||||
$depth = (int)($item['depth'] ?? 0);
|
||||
if ($itemId <= 0 || $depth <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentId = (int)($item['parent_id'] ?? 0);
|
||||
if ($depth === 2) {
|
||||
$parentId = null;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$this->db->update('pp_pages', [
|
||||
'o' => $position,
|
||||
'parent_id' => $parentId,
|
||||
], [
|
||||
'id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pageSave(array $data): ?int
|
||||
{
|
||||
$pageId = (int)($data['id'] ?? 0);
|
||||
$menuId = (int)($data['menu_id'] ?? 0);
|
||||
$parentId = $this->normalizeNullableInt($data['parent_id'] ?? null);
|
||||
$pageType = (int)($data['page_type'] ?? 0);
|
||||
$sortType = (int)($data['sort_type'] ?? 0);
|
||||
$layoutId = (int)($data['layout_id'] ?? 0);
|
||||
$articlesLimit = (int)($data['articles_limit'] ?? 0);
|
||||
$showTitle = $this->toSwitchValue($data['show_title'] ?? 0);
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$start = $this->toSwitchValue($data['start'] ?? 0);
|
||||
$categoryId = $this->normalizeNullableInt($data['category_id'] ?? null);
|
||||
if ($pageType !== 5) {
|
||||
$categoryId = null;
|
||||
}
|
||||
|
||||
if ($pageId <= 0) {
|
||||
$order = $this->maxPageOrder() + 1;
|
||||
$result = $this->db->insert('pp_pages', [
|
||||
'menu_id' => $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle,
|
||||
'status' => $status,
|
||||
'o' => $order,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start,
|
||||
'category_id' => $categoryId,
|
||||
]);
|
||||
|
||||
if (!$result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pageId = (int)$this->db->id();
|
||||
if ($pageId <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_pages', [
|
||||
'menu_id' => $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle,
|
||||
'status' => $status,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start,
|
||||
'category_id' => $categoryId,
|
||||
], [
|
||||
'id' => $pageId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($start === 1) {
|
||||
$this->db->update('pp_pages', ['start' => 0], ['id[!]' => $pageId]);
|
||||
$this->db->update('pp_pages', ['start' => 1], ['id' => $pageId]);
|
||||
}
|
||||
|
||||
$this->db->delete('pp_layouts_pages', ['page_id' => $pageId]);
|
||||
if ($layoutId > 0) {
|
||||
$this->db->insert('pp_layouts_pages', [
|
||||
'layout_id' => $layoutId,
|
||||
'page_id' => $pageId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->saveTranslations($pageId, $pageType, $data);
|
||||
$this->updateSubpagesMenuId($pageId, $menuId);
|
||||
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
return $pageId;
|
||||
}
|
||||
|
||||
public function generateSeoLink(string $title, int $pageId = 0, int $articleId = 0, int $categoryId = 0): string
|
||||
{
|
||||
$base = trim((string)\Shared\Helpers\Helpers::seo($title));
|
||||
if ($base === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$candidate = $base;
|
||||
$suffix = 0;
|
||||
|
||||
while ($this->isSeoLinkUsed('pp_pages_langs', 'page_id', $candidate, $pageId)
|
||||
|| $this->isSeoLinkUsed('pp_articles_langs', 'article_id', $candidate, $articleId)
|
||||
|| $this->isSeoLinkUsed('pp_shop_categories_langs', 'category_id', $candidate, $categoryId)) {
|
||||
$suffix++;
|
||||
$candidate = $base . '-' . $suffix;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
public function pageUrlPreview(int $pageId, string $langId, string $title, string $seoLink, string $defaultLanguageId): string
|
||||
{
|
||||
$url = trim($seoLink) !== ''
|
||||
? '/' . ltrim($seoLink, '/')
|
||||
: '/s-' . $pageId . '-' . \Shared\Helpers\Helpers::seo($title);
|
||||
|
||||
if ($langId !== '' && $langId !== $defaultLanguageId && $url !== '#') {
|
||||
$url = '/' . $langId . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function toggleCookieValue(string $cookieName, int $itemId): void
|
||||
{
|
||||
if ($cookieName === '' || $itemId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$state = [];
|
||||
if (!empty($_COOKIE[$cookieName])) {
|
||||
$decoded = @unserialize((string)$_COOKIE[$cookieName], ['allowed_classes' => false]);
|
||||
if (is_array($decoded)) {
|
||||
$state = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
$state[$itemId] = empty($state[$itemId]) ? 1 : 0;
|
||||
setcookie($cookieName, serialize($state), time() + 3600 * 24 * 365);
|
||||
}
|
||||
|
||||
public function pageTitle(int $pageId): string
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages_langs', [
|
||||
'[><]pp_langs' => ['lang_id' => 'id'],
|
||||
], 'title', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'title[!]' => '',
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
'LIMIT' => 1,
|
||||
]);
|
||||
|
||||
if (!is_array($rows) || !isset($rows[0])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string)$rows[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function pageLanguages(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages_langs', '*', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'title[!]' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function defaultPage(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'menu_id' => 0,
|
||||
'page_type' => 0,
|
||||
'sort_type' => 0,
|
||||
'articles_limit' => 2,
|
||||
'show_title' => 1,
|
||||
'status' => 1,
|
||||
'start' => 0,
|
||||
'parent_id' => null,
|
||||
'category_id' => null,
|
||||
'layout_id' => 0,
|
||||
'languages' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function saveTranslations(int $pageId, int $pageType, array $data): void
|
||||
{
|
||||
$titles = is_array($data['title'] ?? null) ? $data['title'] : [];
|
||||
$seoLinks = is_array($data['seo_link'] ?? null) ? $data['seo_link'] : [];
|
||||
$metaTitles = is_array($data['meta_title'] ?? null) ? $data['meta_title'] : [];
|
||||
$metaDescriptions = is_array($data['meta_description'] ?? null) ? $data['meta_description'] : [];
|
||||
$metaKeywords = is_array($data['meta_keywords'] ?? null) ? $data['meta_keywords'] : [];
|
||||
$noindexValues = is_array($data['noindex'] ?? null) ? $data['noindex'] : [];
|
||||
$pageTitles = is_array($data['page_title'] ?? null) ? $data['page_title'] : [];
|
||||
$links = is_array($data['link'] ?? null) ? $data['link'] : [];
|
||||
$canonicals = is_array($data['canonical'] ?? null) ? $data['canonical'] : [];
|
||||
|
||||
foreach ($titles as $langId => $title) {
|
||||
$langId = (string)$langId;
|
||||
if ($langId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'lang_id' => $langId,
|
||||
'title' => $this->nullIfEmpty($title),
|
||||
'meta_description' => $this->nullIfEmpty($metaDescriptions[$langId] ?? null),
|
||||
'meta_keywords' => $this->nullIfEmpty($metaKeywords[$langId] ?? null),
|
||||
'meta_title' => $this->nullIfEmpty($metaTitles[$langId] ?? null),
|
||||
'seo_link' => $this->nullIfEmpty(\Shared\Helpers\Helpers::seo((string)($seoLinks[$langId] ?? ''))),
|
||||
'noindex' => (int)($noindexValues[$langId] ?? 0),
|
||||
'page_title' => $this->nullIfEmpty($pageTitles[$langId] ?? null),
|
||||
'link' => $pageType === 3 ? $this->nullIfEmpty($links[$langId] ?? null) : null,
|
||||
'canonical' => $this->nullIfEmpty($canonicals[$langId] ?? null),
|
||||
];
|
||||
|
||||
$translationId = (int)$this->db->get('pp_pages_langs', 'id', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($translationId > 0) {
|
||||
$this->db->update('pp_pages_langs', $row, ['id' => $translationId]);
|
||||
} else {
|
||||
$row['page_id'] = $pageId;
|
||||
$this->db->insert('pp_pages_langs', $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubpagesMenuId(int $parentId, int $menuId): void
|
||||
{
|
||||
if ($parentId <= 0 || $menuId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->update('pp_pages', ['menu_id' => $menuId], ['parent_id' => $parentId]);
|
||||
|
||||
$children = $this->db->select('pp_pages', ['id'], ['parent_id' => $parentId]);
|
||||
if (!is_array($children)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($children as $row) {
|
||||
$childId = (int)($row['id'] ?? 0);
|
||||
if ($childId > 0) {
|
||||
$this->updateSubpagesMenuId($childId, $menuId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isSeoLinkUsed(string $table, string $idColumn, string $seoLink, int $exceptId): bool
|
||||
{
|
||||
$where = [
|
||||
'seo_link' => $seoLink,
|
||||
];
|
||||
|
||||
if ($exceptId > 0) {
|
||||
$where[$idColumn . '[!]'] = $exceptId;
|
||||
}
|
||||
|
||||
return (int)$this->db->count($table, ['AND' => $where]) > 0;
|
||||
}
|
||||
|
||||
private function maxPageOrder(): int
|
||||
{
|
||||
$max = $this->db->max('pp_pages', 'o');
|
||||
return $max ? (int)$max : 0;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if ($value === 'on' || $value === '1' || $value === 1 || $value === true) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function normalizeNullableInt($value): ?int
|
||||
{
|
||||
if ($value === null || $value === '' || (int)$value === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
private function nullIfEmpty($value): ?string
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
return $value === '' ? null : $value;
|
||||
}
|
||||
|
||||
// ── Frontend methods ──────────────────────────────────────────
|
||||
|
||||
public function frontPageDetails($id = '', $langId = ''): ?array
|
||||
{
|
||||
$langId = (string)$langId;
|
||||
|
||||
if (!$id) {
|
||||
$id = $this->frontMainPageId();
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "PagesRepository::frontPageDetails:$id:$langId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$page = $this->db->get('pp_pages', '*', ['id' => (int)$id]);
|
||||
if (!is_array($page)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$page['language'] = $this->db->get('pp_pages_langs', '*', ['AND' => ['page_id' => (int)$id, 'lang_id' => $langId]]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
public function frontPageSort(int $pageId)
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "PagesRepository::frontPageSort:$pageId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if ($cached !== false) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$sort = $this->db->get('pp_pages', 'sort_type', ['id' => $pageId]);
|
||||
|
||||
$cacheHandler->set($cacheKey, $sort);
|
||||
return $sort;
|
||||
}
|
||||
|
||||
public function frontMainPageId()
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'PagesRepository::frontMainPageId';
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$id = $this->db->get('pp_pages', 'id', ['AND' => ['status' => 1, 'start' => 1]]);
|
||||
if (!$id) {
|
||||
$id = $this->db->get('pp_pages', 'id', ['status' => 1, 'ORDER' => ['menu_id' => 'ASC', 'o' => 'ASC'], 'LIMIT' => 1]);
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $id);
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function frontLangUrl(int $pageId, string $langId): string
|
||||
{
|
||||
$page = $this->frontPageDetails($pageId, $langId);
|
||||
if (!is_array($page) || !is_array($page['language'] ?? null)) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
$seoLink = $page['language']['seo_link'] ?? '';
|
||||
$title = $page['language']['title'] ?? '';
|
||||
$url = $seoLink ? '/' . $seoLink : '/s-' . $page['id'] . '-' . \Shared\Helpers\Helpers::seo($title);
|
||||
|
||||
$defaultLang = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
|
||||
if ($langId !== $defaultLang && $url !== '#') {
|
||||
$url = '/' . $langId . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function frontMenuDetails(int $menuId, string $langId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "PagesRepository::frontMenuDetails:$menuId:$langId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$menu = $this->db->get('pp_menus', '*', ['id' => (int)$menuId]);
|
||||
if (!is_array($menu)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menu['pages'] = $this->frontMenuPages($menuId, $langId);
|
||||
|
||||
$cacheHandler->set($cacheKey, $menu);
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function frontMenuPages(int $menuId, string $langId, $parentId = null): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "PagesRepository::frontMenuPages:$menuId:$langId:$parentId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$results = $this->db->select('pp_pages', ['id'], [
|
||||
'AND' => ['status' => 1, 'menu_id' => (int)$menuId, 'parent_id' => $parentId],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
$pages = [];
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$page = $this->frontPageDetails($row['id'], $langId);
|
||||
if (is_array($page)) {
|
||||
$page['pages'] = $this->frontMenuPages($menuId, $langId, $row['id']);
|
||||
$pages[] = $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $pages);
|
||||
return $pages;
|
||||
}
|
||||
}
|
||||
370
autoload/Domain/PaymentMethod/PaymentMethodRepository.php
Normal file
370
autoload/Domain/PaymentMethod/PaymentMethodRepository.php
Normal file
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
namespace Domain\PaymentMethod;
|
||||
|
||||
class PaymentMethodRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'spm.id',
|
||||
'name' => 'spm.name',
|
||||
'status' => 'spm.status',
|
||||
'apilo_payment_type_id' => 'spm.apilo_payment_type_id',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'spm.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'spm.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'spm.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_payment_methods AS spm
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
spm.id,
|
||||
spm.name,
|
||||
spm.description,
|
||||
spm.status,
|
||||
spm.apilo_payment_type_id
|
||||
FROM pp_shop_payment_methods AS spm
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, spm.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item = $this->normalizePaymentMethod($item);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $paymentMethodId): ?array
|
||||
{
|
||||
if ($paymentMethodId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$paymentMethod = $this->db->get('pp_shop_payment_methods', '*', ['id' => $paymentMethodId]);
|
||||
if (!is_array($paymentMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizePaymentMethod($paymentMethod);
|
||||
}
|
||||
|
||||
public function save(int $paymentMethodId, array $data): ?int
|
||||
{
|
||||
if ($paymentMethodId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'description' => trim((string)($data['description'] ?? '')),
|
||||
'status' => $this->toSwitchValue($data['status'] ?? 0),
|
||||
'apilo_payment_type_id' => $this->normalizeApiloPaymentTypeId($data['apilo_payment_type_id'] ?? null),
|
||||
];
|
||||
|
||||
$this->db->update('pp_shop_payment_methods', $row, ['id' => $paymentMethodId]);
|
||||
|
||||
return $paymentMethodId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function allActive(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_payment_methods', '*', [
|
||||
'status' => 1,
|
||||
'ORDER' => ['id' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
if (is_array($row)) {
|
||||
$result[] = $this->normalizePaymentMethod($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function allForAdmin(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_payment_methods', '*', [
|
||||
'ORDER' => ['name' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
if (is_array($row)) {
|
||||
$result[] = $this->normalizePaymentMethod($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function findActiveById(int $paymentMethodId): ?array
|
||||
{
|
||||
if ($paymentMethodId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$paymentMethod = $this->db->get('pp_shop_payment_methods', '*', [
|
||||
'AND' => [
|
||||
'id' => $paymentMethodId,
|
||||
'status' => 1,
|
||||
],
|
||||
]);
|
||||
|
||||
if (!is_array($paymentMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizePaymentMethod($paymentMethod);
|
||||
}
|
||||
|
||||
public function isActive(int $paymentMethodId): int
|
||||
{
|
||||
if ($paymentMethodId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$status = $this->db->get('pp_shop_payment_methods', 'status', ['id' => $paymentMethodId]);
|
||||
return $this->toSwitchValue($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string|null
|
||||
*/
|
||||
public function getApiloPaymentTypeId(int $paymentMethodId)
|
||||
{
|
||||
if ($paymentMethodId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $this->db->get('pp_shop_payment_methods', 'apilo_payment_type_id', ['id' => $paymentMethodId]);
|
||||
return $this->normalizeApiloPaymentTypeId($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function forTransport(int $transportMethodId): array
|
||||
{
|
||||
if ($transportMethodId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
spm.id,
|
||||
spm.name,
|
||||
spm.description,
|
||||
spm.status,
|
||||
spm.apilo_payment_type_id
|
||||
FROM pp_shop_payment_methods AS spm
|
||||
INNER JOIN pp_shop_transport_payment_methods AS stpm
|
||||
ON stpm.id_payment_method = spm.id
|
||||
WHERE spm.status = 1
|
||||
AND stpm.id_transport = :transport_id
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, [':transport_id' => $transportMethodId]);
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
if (is_array($row)) {
|
||||
$result[] = $this->normalizePaymentMethod($row);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metody platnosci dla danego transportu — z Redis cache (frontend).
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function paymentMethodsByTransport(int $transportMethodId): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'payment_methods_by_transport' . $transportMethodId;
|
||||
$cached = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($cached) {
|
||||
return unserialize($cached);
|
||||
}
|
||||
|
||||
$result = $this->forTransport($transportMethodId);
|
||||
$cacheHandler->set($cacheKey, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pojedyncza aktywna metoda platnosci — z Redis cache (frontend).
|
||||
*/
|
||||
public function paymentMethodCached(int $paymentMethodId): ?array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'payment_method' . $paymentMethodId;
|
||||
$cached = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($cached) {
|
||||
return unserialize($cached);
|
||||
}
|
||||
|
||||
$result = $this->findActiveById($paymentMethodId);
|
||||
$cacheHandler->set($cacheKey, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wszystkie aktywne metody platnosci — z Redis cache (frontend).
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function paymentMethodsCached(): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'payment_methods';
|
||||
$cached = $cacheHandler->get($cacheKey);
|
||||
|
||||
if ($cached) {
|
||||
return unserialize($cached);
|
||||
}
|
||||
|
||||
$result = $this->allActive();
|
||||
$cacheHandler->set($cacheKey, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function normalizePaymentMethod(array $row): array
|
||||
{
|
||||
$row['id'] = (int)($row['id'] ?? 0);
|
||||
$row['name'] = trim((string)($row['name'] ?? ''));
|
||||
$row['description'] = (string)($row['description'] ?? '');
|
||||
$row['status'] = $this->toSwitchValue($row['status'] ?? 0);
|
||||
$row['apilo_payment_type_id'] = $this->normalizeApiloPaymentTypeId($row['apilo_payment_type_id'] ?? null);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string|null
|
||||
*/
|
||||
private function normalizeApiloPaymentTypeId($value)
|
||||
{
|
||||
if ($value === null || $value === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$text = trim((string)$value);
|
||||
if ($text === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (preg_match('/^-?\d+$/', $text) === 1) {
|
||||
return (int)$text;
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
360
autoload/Domain/Producer/ProducerRepository.php
Normal file
360
autoload/Domain/Producer/ProducerRepository.php
Normal file
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
namespace Domain\Producer;
|
||||
|
||||
class ProducerRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista producentów dla panelu admin (paginowana, filtrowalna, sortowalna).
|
||||
*
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'p.id',
|
||||
'name' => 'p.name',
|
||||
'status' => 'p.status',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'p.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'p.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'p.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_producer AS p
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.status,
|
||||
p.img
|
||||
FROM pp_shop_producer AS p
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, p.id DESC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['id'] = (int)($item['id'] ?? 0);
|
||||
$item['status'] = $this->toSwitchValue($item['status'] ?? 0);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera producenta z tłumaczeniami.
|
||||
*/
|
||||
public function find(int $id): array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return $this->defaultProducer();
|
||||
}
|
||||
|
||||
$producer = $this->db->get('pp_shop_producer', '*', ['id' => $id]);
|
||||
if (!is_array($producer)) {
|
||||
return $this->defaultProducer();
|
||||
}
|
||||
|
||||
$producer['id'] = (int)($producer['id'] ?? 0);
|
||||
$producer['status'] = $this->toSwitchValue($producer['status'] ?? 0);
|
||||
|
||||
// Tłumaczenia
|
||||
$rows = $this->db->select('pp_shop_producer_lang', '*', ['producer_id' => $id]);
|
||||
$languages = [];
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$langId = $row['lang_id'] ?? '';
|
||||
$languages[$langId] = [
|
||||
'description' => $row['description'] ?? null,
|
||||
'data' => $row['data'] ?? null,
|
||||
'meta_title' => $row['meta_title'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
$producer['languages'] = $languages;
|
||||
|
||||
return $producer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje producenta (insert / update) wraz z tłumaczeniami.
|
||||
*
|
||||
* @return int|null ID producenta lub null przy błędzie
|
||||
*/
|
||||
public function save(
|
||||
int $id,
|
||||
string $name,
|
||||
int $status,
|
||||
?string $img,
|
||||
array $description,
|
||||
array $data,
|
||||
array $metaTitle,
|
||||
array $langs
|
||||
): ?int {
|
||||
$row = [
|
||||
'name' => trim($name),
|
||||
'status' => $status === 1 ? 1 : 0,
|
||||
'img' => $img,
|
||||
];
|
||||
|
||||
if ($id <= 0) {
|
||||
$this->db->insert('pp_shop_producer', $row);
|
||||
$id = (int)$this->db->id();
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_shop_producer', $row, ['id' => $id]);
|
||||
}
|
||||
|
||||
// Tłumaczenia
|
||||
foreach ($langs as $lg) {
|
||||
$langId = $lg['id'] ?? '';
|
||||
$translationData = [
|
||||
'description' => $description[$langId] ?? null,
|
||||
'data' => $data[$langId] ?? null,
|
||||
'meta_title' => $metaTitle[$langId] ?? null,
|
||||
];
|
||||
|
||||
$translationId = $this->db->get(
|
||||
'pp_shop_producer_lang',
|
||||
'id',
|
||||
['AND' => ['producer_id' => $id, 'lang_id' => $langId]]
|
||||
);
|
||||
|
||||
if ($translationId) {
|
||||
$this->db->update('pp_shop_producer_lang', $translationData, ['id' => $translationId]);
|
||||
} else {
|
||||
$this->db->insert('pp_shop_producer_lang', array_merge($translationData, [
|
||||
'producer_id' => $id,
|
||||
'lang_id' => $langId,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuwa producenta (kaskadowo z pp_shop_producer_lang przez FK).
|
||||
*/
|
||||
public function delete(int $id): bool
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = (bool)$this->db->delete('pp_shop_producer', ['id' => $id]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wszystkie producenty (do select w edycji produktu).
|
||||
*
|
||||
* @return array<int, array{id: int, name: string}>
|
||||
*/
|
||||
public function allProducers(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_producer', ['id', 'name'], ['ORDER' => ['name' => 'ASC']]);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$producers = [];
|
||||
foreach ($rows as $row) {
|
||||
$producers[] = [
|
||||
'id' => (int)($row['id'] ?? 0),
|
||||
'name' => (string)($row['name'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
return $producers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera producenta z tłumaczeniami dla danego języka (frontend).
|
||||
*/
|
||||
public function findForFrontend(int $id, string $langId): ?array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$producer = $this->db->get('pp_shop_producer', '*', ['id' => $id]);
|
||||
if (!is_array($producer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$producer['id'] = (int)($producer['id'] ?? 0);
|
||||
|
||||
$langRow = $this->db->get('pp_shop_producer_lang', '*', [
|
||||
'AND' => ['producer_id' => $id, 'lang_id' => $langId],
|
||||
]);
|
||||
|
||||
$producer['languages'] = [];
|
||||
if (is_array($langRow)) {
|
||||
$producer['languages'][$langId] = [
|
||||
'description' => $langRow['description'] ?? null,
|
||||
'data' => $langRow['data'] ?? null,
|
||||
'meta_title' => $langRow['meta_title'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
return $producer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produkty producenta (paginowane) — frontend.
|
||||
*
|
||||
* @return array{products: array, ls: int}
|
||||
*/
|
||||
public function producerProducts(int $producerId, int $perPage = 12, int $page = 1): array
|
||||
{
|
||||
$count = $this->db->count('pp_shop_products', [
|
||||
'AND' => ['producer_id' => $producerId, 'status' => 1],
|
||||
]);
|
||||
|
||||
$totalPages = max(1, (int)ceil($count / $perPage));
|
||||
$page = max(1, min($page, $totalPages));
|
||||
$offset = $perPage * ($page - 1);
|
||||
|
||||
$products = $this->db->select('pp_shop_products', 'id', [
|
||||
'AND' => ['producer_id' => $producerId, 'status' => 1],
|
||||
'LIMIT' => [$offset, $perPage],
|
||||
]);
|
||||
|
||||
return [
|
||||
'products' => is_array($products) ? $products : [],
|
||||
'ls' => $totalPages,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktywni producenci (frontend lista).
|
||||
*
|
||||
* @return array<int>
|
||||
*/
|
||||
public function allActiveIds(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_producer', 'id', [
|
||||
'status' => 1,
|
||||
'ORDER' => ['name' => 'ASC'],
|
||||
]);
|
||||
|
||||
return is_array($rows) ? array_map('intval', $rows) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktywni producenci z pelnym danymi (frontend lista).
|
||||
*
|
||||
* @return array<int, array{id: int, name: string, img: string|null}>
|
||||
*/
|
||||
public function allActiveProducers(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_producer', ['id', 'name', 'img'], [
|
||||
'status' => 1,
|
||||
'ORDER' => ['name' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$producers = [];
|
||||
foreach ($rows as $row) {
|
||||
$producers[] = [
|
||||
'id' => (int)($row['id'] ?? 0),
|
||||
'name' => (string)($row['name'] ?? ''),
|
||||
'img' => $row['img'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
return $producers;
|
||||
}
|
||||
|
||||
private function defaultProducer(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
'img' => null,
|
||||
'languages' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
3119
autoload/Domain/Product/ProductRepository.php
Normal file
3119
autoload/Domain/Product/ProductRepository.php
Normal file
File diff suppressed because it is too large
Load Diff
250
autoload/Domain/ProductSet/ProductSetRepository.php
Normal file
250
autoload/Domain/ProductSet/ProductSetRepository.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
namespace Domain\ProductSet;
|
||||
|
||||
class ProductSetRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'ps.id',
|
||||
'name' => 'ps.name',
|
||||
'status' => 'ps.status',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'ps.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'ps.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'ps.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_product_sets AS ps
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
ps.id,
|
||||
ps.name,
|
||||
ps.status
|
||||
FROM pp_shop_product_sets AS ps
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, ps.id DESC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['id'] = (int)($item['id'] ?? 0);
|
||||
$item['status'] = $this->toSwitchValue($item['status'] ?? 0);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $id): array
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return $this->defaultSet();
|
||||
}
|
||||
|
||||
$set = $this->db->get('pp_shop_product_sets', '*', ['id' => $id]);
|
||||
if (!is_array($set)) {
|
||||
return $this->defaultSet();
|
||||
}
|
||||
|
||||
$set['id'] = (int)($set['id'] ?? 0);
|
||||
$set['status'] = $this->toSwitchValue($set['status'] ?? 0);
|
||||
|
||||
$products = $this->db->select('pp_shop_product_sets_products', 'product_id', ['set_id' => $id]);
|
||||
$set['products'] = is_array($products) ? array_map('intval', $products) : [];
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
public function save(int $id, string $name, int $status, array $productIds): ?int
|
||||
{
|
||||
$row = [
|
||||
'name' => trim($name),
|
||||
'status' => $status === 1 ? 1 : 0,
|
||||
];
|
||||
|
||||
if ($id <= 0) {
|
||||
$this->db->insert('pp_shop_product_sets', $row);
|
||||
$id = (int)$this->db->id();
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_shop_product_sets', $row, ['id' => $id]);
|
||||
}
|
||||
|
||||
$this->syncProducts($id, $productIds);
|
||||
$this->clearTempAndCache();
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function delete(int $id): bool
|
||||
{
|
||||
if ($id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->delete('pp_shop_product_sets_products', ['set_id' => $id]);
|
||||
$result = (bool)$this->db->delete('pp_shop_product_sets', ['id' => $id]);
|
||||
|
||||
if ($result) {
|
||||
$this->clearTempAndCache();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: int, name: string}>
|
||||
*/
|
||||
public function allSets(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_product_sets', ['id', 'name'], ['ORDER' => ['name' => 'ASC']]);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sets = [];
|
||||
foreach ($rows as $row) {
|
||||
$sets[] = [
|
||||
'id' => (int)($row['id'] ?? 0),
|
||||
'name' => (string)($row['name'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
return $sets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function allProductsMap(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_products', 'id', ['parent_id' => null]);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$products = [];
|
||||
foreach ($rows as $productId) {
|
||||
$name = $this->db->get('pp_shop_products_langs', 'name', [
|
||||
'AND' => ['product_id' => $productId, 'lang_id' => 'pl'],
|
||||
]);
|
||||
if ($name) {
|
||||
$products[(int)$productId] = (string)$name;
|
||||
}
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
private function syncProducts(int $setId, array $productIds): void
|
||||
{
|
||||
$this->db->delete('pp_shop_product_sets_products', ['set_id' => $setId]);
|
||||
|
||||
$seen = [];
|
||||
foreach ($productIds as $productId) {
|
||||
$pid = (int)$productId;
|
||||
if ($pid > 0 && !isset($seen[$pid])) {
|
||||
$this->db->insert('pp_shop_product_sets_products', [
|
||||
'set_id' => $setId,
|
||||
'product_id' => $pid,
|
||||
]);
|
||||
$seen[$pid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function defaultSet(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
'products' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function clearTempAndCache(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::delete_dir('../thumbs/');
|
||||
}
|
||||
}
|
||||
710
autoload/Domain/Promotion/PromotionRepository.php
Normal file
710
autoload/Domain/Promotion/PromotionRepository.php
Normal file
@@ -0,0 +1,710 @@
|
||||
<?php
|
||||
namespace Domain\Promotion;
|
||||
|
||||
class PromotionRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
private ?string $defaultLangId = null;
|
||||
|
||||
public static $condition_type = [
|
||||
1 => 'Rabat procentowy na produkty z kategorii 1 jeżeli w koszyku jest produkt z kategorii 2',
|
||||
2 => 'Rabat procentowy na produkty z kategorii 1 i 2',
|
||||
3 => 'Najtańszy produkt w koszyku (z wybranych kategorii) za X zł',
|
||||
4 => 'Rabat procentowy na cały koszyk',
|
||||
5 => 'Rabat procentowy na produkty z kategorii 1 lub 2',
|
||||
];
|
||||
|
||||
public static $discount_type = [ 1 => 'Rabat procentowy' ];
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'id',
|
||||
string $sortDir = 'DESC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'sp.id',
|
||||
'name' => 'sp.name',
|
||||
'status' => 'sp.status',
|
||||
'condition_type' => 'sp.condition_type',
|
||||
'date_from' => 'sp.date_from',
|
||||
'date_to' => 'sp.date_to',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'sp.id';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'ASC' ? 'ASC' : 'DESC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'sp.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'sp.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_promotion AS sp
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
sp.id,
|
||||
sp.name,
|
||||
sp.status,
|
||||
sp.condition_type,
|
||||
sp.discount_type,
|
||||
sp.amount,
|
||||
sp.date_from,
|
||||
sp.date_to,
|
||||
sp.include_coupon,
|
||||
sp.include_product_promo,
|
||||
sp.min_product_count,
|
||||
sp.price_cheapest_product
|
||||
FROM pp_shop_promotion AS sp
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, sp.id DESC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $promotionId): array
|
||||
{
|
||||
if ($promotionId <= 0) {
|
||||
return $this->defaultPromotion();
|
||||
}
|
||||
|
||||
$promotion = $this->db->get('pp_shop_promotion', '*', ['id' => $promotionId]);
|
||||
if (!is_array($promotion)) {
|
||||
return $this->defaultPromotion();
|
||||
}
|
||||
|
||||
$promotion['id'] = (int)($promotion['id'] ?? 0);
|
||||
$promotion['status'] = $this->toSwitchValue($promotion['status'] ?? 0);
|
||||
$promotion['include_coupon'] = $this->toSwitchValue($promotion['include_coupon'] ?? 0);
|
||||
$promotion['include_product_promo'] = $this->toSwitchValue($promotion['include_product_promo'] ?? 0);
|
||||
$promotion['condition_type'] = (int)($promotion['condition_type'] ?? 1);
|
||||
$promotion['discount_type'] = (int)($promotion['discount_type'] ?? 1);
|
||||
$promotion['categories'] = $this->decodeIdList($promotion['categories'] ?? null);
|
||||
$promotion['condition_categories'] = $this->decodeIdList($promotion['condition_categories'] ?? null);
|
||||
|
||||
return $promotion;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$promotionId = (int)($data['id'] ?? 0);
|
||||
|
||||
$row = [
|
||||
'name' => trim((string)($data['name'] ?? '')),
|
||||
'status' => $this->toSwitchValue($data['status'] ?? 0),
|
||||
'condition_type' => (int)($data['condition_type'] ?? 1),
|
||||
'discount_type' => (int)($data['discount_type'] ?? 1),
|
||||
'amount' => $this->toNullableNumeric($data['amount'] ?? null),
|
||||
'date_from' => $this->toNullableDate($data['date_from'] ?? null),
|
||||
'date_to' => $this->toNullableDate($data['date_to'] ?? null),
|
||||
'categories' => $this->encodeIdList($data['categories'] ?? null),
|
||||
'condition_categories' => $this->encodeIdList($data['condition_categories'] ?? null),
|
||||
'include_coupon' => $this->toSwitchValue($data['include_coupon'] ?? 0),
|
||||
'include_product_promo' => $this->toSwitchValue($data['include_product_promo'] ?? 0),
|
||||
'min_product_count' => $this->toNullableInt($data['min_product_count'] ?? null),
|
||||
'price_cheapest_product' => $this->toNullableNumeric($data['price_cheapest_product'] ?? null),
|
||||
];
|
||||
|
||||
if ($promotionId <= 0) {
|
||||
$this->db->insert('pp_shop_promotion', $row);
|
||||
$id = (int)$this->db->id();
|
||||
if ($id <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->invalidateActivePromotionsCache();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return $id;
|
||||
}
|
||||
|
||||
$this->db->update('pp_shop_promotion', $row, ['id' => $promotionId]);
|
||||
$this->invalidateActivePromotionsCache();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
return $promotionId;
|
||||
}
|
||||
|
||||
public function delete(int $promotionId): bool
|
||||
{
|
||||
if ($promotionId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$deleted = $this->db->delete('pp_shop_promotion', ['id' => $promotionId]);
|
||||
$ok = (bool)$deleted;
|
||||
|
||||
if ($ok) {
|
||||
$this->invalidateActivePromotionsCache();
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function categoriesTree($parentId = null): array
|
||||
{
|
||||
$rows = $this->db->select('pp_shop_categories', ['id'], [
|
||||
'parent_id' => $parentId,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
foreach ($rows as $row) {
|
||||
$categoryId = (int)($row['id'] ?? 0);
|
||||
if ($categoryId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$category = $this->db->get('pp_shop_categories', '*', ['id' => $categoryId]);
|
||||
if (!is_array($category)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations = $this->db->select('pp_shop_categories_langs', '*', ['category_id' => $categoryId]);
|
||||
$category['languages'] = [];
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $translation) {
|
||||
$langId = (string)($translation['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$category['languages'][$langId] = $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$category['title'] = $this->categoryTitle($category['languages']);
|
||||
$category['subcategories'] = $this->categoriesTree($categoryId);
|
||||
$categories[] = $category;
|
||||
}
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
private function defaultPromotion(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
'condition_type' => 1,
|
||||
'discount_type' => 1,
|
||||
'amount' => null,
|
||||
'date_from' => null,
|
||||
'date_to' => null,
|
||||
'categories' => [],
|
||||
'condition_categories' => [],
|
||||
'include_coupon' => 0,
|
||||
'include_product_promo' => 0,
|
||||
'min_product_count' => null,
|
||||
'price_cheapest_product' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function toNullableInt($value): ?int
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = trim($value);
|
||||
if ($value === '') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$intValue = (int)$value;
|
||||
return $intValue > 0 ? $intValue : null;
|
||||
}
|
||||
|
||||
private function toNullableNumeric($value): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stringValue = trim((string)$value);
|
||||
if ($stringValue === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str_replace(',', '.', $stringValue);
|
||||
}
|
||||
|
||||
private function toNullableDate($value): ?string
|
||||
{
|
||||
$date = trim((string)$value);
|
||||
if ($date === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
private function encodeIdList($values): ?string
|
||||
{
|
||||
$ids = $this->normalizeIdList($values);
|
||||
if (empty($ids)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_encode($ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function decodeIdList($raw): array
|
||||
{
|
||||
if (is_array($raw)) {
|
||||
return $this->normalizeIdList($raw);
|
||||
}
|
||||
|
||||
$text = trim((string)$raw);
|
||||
if ($text === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($text, true);
|
||||
if (!is_array($decoded)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->normalizeIdList($decoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function normalizeIdList($values): array
|
||||
{
|
||||
if ($values === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!is_array($values)) {
|
||||
$text = trim((string)$values);
|
||||
if ($text === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (strpos($text, ',') !== false) {
|
||||
$values = explode(',', $text);
|
||||
} else {
|
||||
$values = [$text];
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($values as $value) {
|
||||
$id = (int)$value;
|
||||
if ($id > 0) {
|
||||
$ids[$id] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($ids);
|
||||
}
|
||||
|
||||
private function categoryTitle(array $languages): string
|
||||
{
|
||||
$defaultLang = $this->defaultLanguageId();
|
||||
if ($defaultLang !== '' && isset($languages[$defaultLang]['title'])) {
|
||||
$title = trim((string)$languages[$defaultLang]['title']);
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$title = trim((string)($language['title'] ?? ''));
|
||||
if ($title !== '') {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function defaultLanguageId(): string
|
||||
{
|
||||
if ($this->defaultLangId !== null) {
|
||||
return $this->defaultLangId;
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_langs', ['id', 'start', 'o'], [
|
||||
'status' => 1,
|
||||
'ORDER' => ['start' => 'DESC', 'o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (is_array($rows) && !empty($rows)) {
|
||||
$this->defaultLangId = (string)($rows[0]['id'] ?? '');
|
||||
} else {
|
||||
$this->defaultLangId = '';
|
||||
}
|
||||
|
||||
return $this->defaultLangId;
|
||||
}
|
||||
|
||||
private function invalidateActivePromotionsCache(): void
|
||||
{
|
||||
if (!class_exists('\Shared\Cache\CacheHandler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$cache = new \Shared\Cache\CacheHandler();
|
||||
if (method_exists($cache, 'delete')) {
|
||||
$cache->delete('PromotionRepository::getActivePromotions');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Cache invalidation should not block save/delete.
|
||||
}
|
||||
}
|
||||
|
||||
public function getActivePromotions()
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "PromotionRepository::getActivePromotions";
|
||||
|
||||
$objectData = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( !$objectData )
|
||||
{
|
||||
$results = $this->db->select( 'pp_shop_promotion', 'id', [ 'AND' => [ 'status' => 1, 'OR #date_from' => [ 'date_from' => null, 'date_from[<=]' => date( 'Y-m-d' ) ], 'OR #date_to' => [ 'date_to' => null, 'date_to[>=]' => date( 'Y-m-d' ) ] ], 'ORDER' => [ 'id' => 'DESC' ] ] );
|
||||
|
||||
$cacheHandler->set( $cacheKey, $results );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unserialize( $objectData );
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function findPromotion( $basket )
|
||||
{
|
||||
if ( !is_array( $basket ) || empty( $basket ) )
|
||||
return is_array( $basket ) ? $basket : [];
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
unset( $basket[$key]['discount_type'] );
|
||||
unset( $basket[$key]['discount_amount'] );
|
||||
unset( $basket[$key]['discount_include_coupon'] );
|
||||
unset( $basket[$key]['include_product_promo'] );
|
||||
}
|
||||
|
||||
$basket_tmp = $basket;
|
||||
|
||||
$results = $this->getActivePromotions();
|
||||
if ( is_array( $results ) and count( $results ) )
|
||||
{
|
||||
foreach ( $results as $row )
|
||||
{
|
||||
$promotion = $this->find( (int)$row );
|
||||
|
||||
if ( $promotion['id'] <= 0 )
|
||||
continue;
|
||||
|
||||
if ( $promotion['condition_type'] == 4 )
|
||||
return $this->applyTypeWholeBasket( $basket_tmp, $promotion );
|
||||
|
||||
if ( $promotion['condition_type'] == 3 )
|
||||
return $this->applyTypeCheapestProduct( $basket_tmp, $promotion );
|
||||
|
||||
if ( $promotion['condition_type'] == 5 )
|
||||
return $this->applyTypeCategoriesOr( $basket_tmp, $promotion );
|
||||
|
||||
if ( $promotion['condition_type'] == 2 )
|
||||
return $this->applyTypeCategoriesAnd( $basket_tmp, $promotion );
|
||||
|
||||
if ( $promotion['condition_type'] == 1 )
|
||||
return $this->applyTypeCategoryCondition( $basket_tmp, $promotion );
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Frontend: basket promotion logic (migrated from front\factory\ShopPromotion)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Promocja na cały koszyk (condition_type=4)
|
||||
*/
|
||||
public function applyTypeWholeBasket(array $basket, $promotion): array
|
||||
{
|
||||
$productRepo = new \Domain\Product\ProductRepository( $this->db );
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_promotion = $productRepo->isProductOnPromotion( $val['product-id'] );
|
||||
|
||||
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
$basket[$key]['discount_type'] = $promotion['discount_type'];
|
||||
$basket[$key]['discount_amount'] = $promotion['amount'];
|
||||
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
|
||||
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promocja na najtańszy produkt z kategorii 1 lub 2 (condition_type=3)
|
||||
*/
|
||||
public function applyTypeCheapestProduct(array $basket, $promotion): array
|
||||
{
|
||||
$productRepo = new \Domain\Product\ProductRepository( $this->db );
|
||||
$condition_1 = false;
|
||||
$categories = $promotion['categories'];
|
||||
|
||||
if ( is_array( $categories ) and is_array( $categories ) )
|
||||
{
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_promotion = $productRepo->isProductOnPromotion( $val['product-id'] );
|
||||
|
||||
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( !$condition_1[$key] and in_array( $category_tmp['category_id'], $categories ) )
|
||||
$condition_1[$key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $condition_1 ) >= $promotion['min_product_count'] )
|
||||
{
|
||||
$cheapest_position = false;
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$price = $productRepo->getPrice( $val['product-id'] );
|
||||
if ( !$cheapest_position or $cheapest_position['price'] > $price )
|
||||
{
|
||||
$cheapest_position['price'] = $price;
|
||||
$cheapest_position['key'] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
$basket[$cheapest_position['key']]['quantity'] = 1;
|
||||
$basket[$cheapest_position['key']]['discount_type'] = 3;
|
||||
$basket[$cheapest_position['key']]['discount_amount'] = $promotion['price_cheapest_product'];
|
||||
$basket[$cheapest_position['key']]['discount_include_coupon'] = $promotion['include_coupon'];
|
||||
$basket[$cheapest_position['key']]['include_product_promo'] = $promotion['include_product_promo'];
|
||||
}
|
||||
|
||||
return $basket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promocja na wszystkie produkty z kategorii 1 lub 2 (condition_type=5)
|
||||
*/
|
||||
public function applyTypeCategoriesOr(array $basket, $promotion): array
|
||||
{
|
||||
$productRepo = new \Domain\Product\ProductRepository( $this->db );
|
||||
$categories = $promotion['categories'];
|
||||
$condition_categories = $promotion['condition_categories'];
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_promotion = $productRepo->isProductOnPromotion( $val['product-id'] );
|
||||
|
||||
if ( !$product_promotion or $product_promotion and $promotion['include_product_promo'] )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp['category_id'], $condition_categories ) or in_array( $category_tmp['category_id'], $categories ) )
|
||||
{
|
||||
$basket[$key]['discount_type'] = $promotion['discount_type'];
|
||||
$basket[$key]['discount_amount'] = $promotion['amount'];
|
||||
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
|
||||
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promocja na produkty z kategorii 1 i 2 (condition_type=2)
|
||||
*/
|
||||
public function applyTypeCategoriesAnd(array $basket, $promotion): array
|
||||
{
|
||||
$productRepo = new \Domain\Product\ProductRepository( $this->db );
|
||||
$condition_1 = false;
|
||||
$condition_2 = false;
|
||||
|
||||
$categories = $promotion['categories'];
|
||||
$condition_categories = $promotion['condition_categories'];
|
||||
|
||||
if ( is_array( $condition_categories ) and is_array( $categories ) )
|
||||
{
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( !$condition_1 and in_array( $category_tmp['category_id'], $condition_categories ) )
|
||||
{
|
||||
$condition_1 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( !$condition_2 and in_array( $category_tmp['category_id'], $categories ) )
|
||||
$condition_2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $condition_1 and $condition_2 )
|
||||
{
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp['category_id'], $categories ) or in_array( $category_tmp['category_id'], $condition_categories ) )
|
||||
{
|
||||
$basket[$key]['discount_type'] = $promotion['discount_type'];
|
||||
$basket[$key]['discount_amount'] = $promotion['amount'];
|
||||
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
|
||||
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rabat procentowy na produkty z kategorii I jeżeli w koszyku jest produkt z kategorii II (condition_type=1)
|
||||
*/
|
||||
public function applyTypeCategoryCondition(array $basket, $promotion): array
|
||||
{
|
||||
$productRepo = new \Domain\Product\ProductRepository( $this->db );
|
||||
$condition = false;
|
||||
$categories = $promotion['categories'];
|
||||
$condition_categories = $promotion['condition_categories'];
|
||||
|
||||
if ( is_array( $condition_categories ) and is_array( $categories ) )
|
||||
{
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp['category_id'], $condition_categories ) )
|
||||
{
|
||||
$condition = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $condition )
|
||||
{
|
||||
foreach ( $basket as $key => $val )
|
||||
{
|
||||
$product_categories = $productRepo->productCategoriesFront( (int) $val['product-id'] );
|
||||
foreach ( $product_categories as $category_tmp )
|
||||
{
|
||||
if ( in_array( $category_tmp['category_id'], $categories ) )
|
||||
{
|
||||
$basket[$key]['discount_type'] = $promotion['discount_type'];
|
||||
$basket[$key]['discount_amount'] = $promotion['amount'];
|
||||
$basket[$key]['discount_include_coupon'] = $promotion['include_coupon'];
|
||||
$basket[$key]['include_product_promo'] = $promotion['include_product_promo'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $basket;
|
||||
}
|
||||
}
|
||||
345
autoload/Domain/Scontainers/ScontainersRepository.php
Normal file
345
autoload/Domain/Scontainers/ScontainersRepository.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
namespace Domain\Scontainers;
|
||||
|
||||
class ScontainersRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'id',
|
||||
string $sortDir = 'DESC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'q1.id',
|
||||
'title' => 'q1.title',
|
||||
'status' => 'q1.status',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'q1.id';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'ASC' ? 'ASC' : 'DESC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$title = trim((string)($filters['title'] ?? ''));
|
||||
if ($title !== '') {
|
||||
if (strlen($title) > 255) {
|
||||
$title = substr($title, 0, 255);
|
||||
}
|
||||
$where[] = 'q1.title LIKE :title';
|
||||
$params[':title'] = '%' . $title . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'q1.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
$baseSelect = $this->baseListSelect();
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM ({$baseSelect}) AS q1
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT q1.*
|
||||
FROM ({$baseSelect}) AS q1
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, q1.id DESC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $containerId): array
|
||||
{
|
||||
if ($containerId <= 0) {
|
||||
return $this->defaultContainer();
|
||||
}
|
||||
|
||||
$container = $this->db->get('pp_scontainers', '*', ['id' => $containerId]);
|
||||
if (!is_array($container)) {
|
||||
return $this->defaultContainer();
|
||||
}
|
||||
|
||||
$container['languages'] = $this->translationsMap($containerId);
|
||||
return $container;
|
||||
}
|
||||
|
||||
public function detailsForLanguage(int $containerId, string $langId): ?array
|
||||
{
|
||||
if ($containerId <= 0 || trim($langId) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$container = $this->db->get('pp_scontainers', '*', ['id' => $containerId]);
|
||||
if (!is_array($container)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$translation = $this->db->get('pp_scontainers_langs', '*', [
|
||||
'AND' => [
|
||||
'container_id' => $containerId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
$container['languages'] = is_array($translation) ? $translation : [
|
||||
'lang_id' => $langId,
|
||||
'title' => '',
|
||||
'text' => '',
|
||||
];
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$containerId = (int)($data['id'] ?? 0);
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$showTitle = $this->toSwitchValue($data['show_title'] ?? 0);
|
||||
$translations = $this->extractTranslations($data);
|
||||
|
||||
if ($containerId <= 0) {
|
||||
$this->db->insert('pp_scontainers', [
|
||||
'status' => $status,
|
||||
'show_title' => $showTitle,
|
||||
]);
|
||||
$containerId = (int)$this->db->id();
|
||||
if ($containerId <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_scontainers', [
|
||||
'status' => $status,
|
||||
'show_title' => $showTitle,
|
||||
], [
|
||||
'id' => $containerId,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($translations as $langId => $row) {
|
||||
$translationId = $this->db->get('pp_scontainers_langs', 'id', [
|
||||
'AND' => [
|
||||
'container_id' => $containerId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($translationId) {
|
||||
$this->db->update('pp_scontainers_langs', [
|
||||
'title' => (string)($row['title'] ?? ''),
|
||||
'text' => (string)($row['text'] ?? ''),
|
||||
], [
|
||||
'id' => (int)$translationId,
|
||||
]);
|
||||
} else {
|
||||
$this->db->insert('pp_scontainers_langs', [
|
||||
'container_id' => $containerId,
|
||||
'lang_id' => $langId,
|
||||
'title' => (string)($row['title'] ?? ''),
|
||||
'text' => (string)($row['text'] ?? ''),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
$this->clearFrontCache($containerId);
|
||||
|
||||
return $containerId;
|
||||
}
|
||||
|
||||
public function delete(int $containerId): bool
|
||||
{
|
||||
if ($containerId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = (bool)$this->db->delete('pp_scontainers', ['id' => $containerId]);
|
||||
if ($result) {
|
||||
$this->clearFrontCache($containerId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function baseListSelect(): string
|
||||
{
|
||||
return "
|
||||
SELECT
|
||||
ps.id,
|
||||
ps.status,
|
||||
(
|
||||
SELECT psl.title
|
||||
FROM pp_scontainers_langs AS psl
|
||||
JOIN pp_langs AS pl ON psl.lang_id = pl.id
|
||||
WHERE psl.container_id = ps.id
|
||||
AND psl.title <> ''
|
||||
ORDER BY pl.o ASC
|
||||
LIMIT 1
|
||||
) AS title
|
||||
FROM pp_scontainers AS ps
|
||||
";
|
||||
}
|
||||
|
||||
// ── Frontend methods ──────────────────────────────────────────
|
||||
|
||||
public function frontScontainerDetails(int $scontainerId, string $langId): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "ScontainersRepository::frontScontainerDetails:$scontainerId";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
$cached = @unserialize($objectData);
|
||||
if (is_array($cached)) {
|
||||
return $cached;
|
||||
}
|
||||
$cacheHandler->delete($cacheKey);
|
||||
}
|
||||
|
||||
$scontainer = $this->detailsForLanguage($scontainerId, $langId);
|
||||
|
||||
if (!is_array($scontainer)) {
|
||||
$scontainer = [
|
||||
'id' => $scontainerId,
|
||||
'status' => 0,
|
||||
'show_title' => 0,
|
||||
'languages' => [
|
||||
'lang_id' => $langId,
|
||||
'title' => '',
|
||||
'text' => '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $scontainer);
|
||||
return $scontainer;
|
||||
}
|
||||
|
||||
private function clearFrontCache(int $containerId): void
|
||||
{
|
||||
if ($containerId <= 0 || !class_exists('\Shared\Cache\CacheHandler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheHandler->delete('ScontainersRepository::frontScontainerDetails:' . $containerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
private function translationsMap(int $containerId): array
|
||||
{
|
||||
$rows = $this->db->select('pp_scontainers_langs', '*', ['container_id' => $containerId]);
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$langId = (string)($row['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$result[$langId] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string>>
|
||||
*/
|
||||
private function extractTranslations(array $data): array
|
||||
{
|
||||
$translations = [];
|
||||
|
||||
if (isset($data['translations']) && is_array($data['translations'])) {
|
||||
foreach ($data['translations'] as $langId => $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$safeLangId = trim((string)$langId);
|
||||
if ($safeLangId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translations[$safeLangId] = [
|
||||
'title' => (string)($row['title'] ?? ''),
|
||||
'text' => (string)($row['text'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$legacyTitles = isset($data['title']) && is_array($data['title']) ? $data['title'] : [];
|
||||
$legacyTexts = isset($data['text']) && is_array($data['text']) ? $data['text'] : [];
|
||||
|
||||
foreach ($legacyTitles as $langId => $title) {
|
||||
$safeLangId = trim((string)$langId);
|
||||
if ($safeLangId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($translations[$safeLangId])) {
|
||||
$translations[$safeLangId] = [
|
||||
'title' => '',
|
||||
'text' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$translations[$safeLangId]['title'] = (string)$title;
|
||||
$translations[$safeLangId]['text'] = (string)($legacyTexts[$safeLangId] ?? '');
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
return ($value === 'on' || $value === 1 || $value === '1' || $value === true) ? 1 : 0;
|
||||
}
|
||||
|
||||
private function defaultContainer(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'status' => 1,
|
||||
'show_title' => 0,
|
||||
'languages' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
213
autoload/Domain/Settings/SettingsRepository.php
Normal file
213
autoload/Domain/Settings/SettingsRepository.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
namespace Domain\Settings;
|
||||
|
||||
/**
|
||||
* Repozytorium ustawien — wspolne dla admin i frontendu.
|
||||
*/
|
||||
class SettingsRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct($db = null)
|
||||
{
|
||||
if ($db) {
|
||||
$this->db = $db;
|
||||
return;
|
||||
}
|
||||
|
||||
global $mdb;
|
||||
$this->db = $mdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis ustawien.
|
||||
*
|
||||
* @param array $values Tablica wartosci z formularza
|
||||
* @return array ['status' => string, 'msg' => string]
|
||||
*/
|
||||
public function saveSettings(array $values): array
|
||||
{
|
||||
$currentSettings = $this->getSettings();
|
||||
|
||||
$settingsToSave = [
|
||||
'firm_name' => $values['firm_name'] ?? '',
|
||||
'firm_adress' => $values['firm_adress'] ?? '',
|
||||
'additional_info' => $values['additional_info'] ?? '',
|
||||
'contact_form' => $this->isEnabled($values['contact_form'] ?? null) ? 1 : 0,
|
||||
'contact_email' => $values['contact_email'] ?? '',
|
||||
'email_host' => $values['email_host'] ?? '',
|
||||
'email_port' => $values['email_port'] ?? '',
|
||||
'email_login' => $values['email_login'] ?? '',
|
||||
'email_password' => $values['email_password'] ?? '',
|
||||
'google_maps' => $values['google_maps'] ?? '',
|
||||
'facebook_link' => $values['facebook_link'] ?? '',
|
||||
'statistic_code' => $values['statistic_code'] ?? '',
|
||||
'htaccess' => $values['htaccess'] ?? '',
|
||||
'robots' => $values['robots'] ?? '',
|
||||
'shop_bank_account_info' => $values['shop_bank_account_info'] ?? '',
|
||||
'update' => $this->isEnabled($values['update'] ?? null) ? 1 : 0,
|
||||
'boot_animation' => $values['boot_animation'] ?? '',
|
||||
// Te pola sa edytowane w module newsletter i musza zostac zachowane.
|
||||
'newsletter_header' => $currentSettings['newsletter_header'] ?? '',
|
||||
'newsletter_footer' => $currentSettings['newsletter_footer'] ?? '',
|
||||
'hotpay_api' => $values['hotpay_api'] ?? '',
|
||||
|
||||
'devel' => $this->isEnabled($values['devel'] ?? null) ? 1 : 0,
|
||||
'ssl' => $this->isEnabled($values['ssl'] ?? null) ? 1 : 0,
|
||||
'htaccess_cache' => $this->isEnabled($values['htaccess_cache'] ?? null) ? 1 : 0,
|
||||
'free_delivery' => $values['free_delivery'] ?? '',
|
||||
'przelewy24_sandbox' => $this->isEnabled($values['przelewy24_sandbox'] ?? null) ? 1 : 0,
|
||||
'przelewy24_merchant_id' => $values['przelewy24_merchant_id'] ?? '',
|
||||
'przelewy24_crc_key' => $values['przelewy24_crc_key'] ?? '',
|
||||
'update_key' => $values['update_key'] ?? '',
|
||||
'tpay_id' => $values['tpay_id'] ?? '',
|
||||
'tpay_sandbox' => $this->isEnabled($values['tpay_sandbox'] ?? null) ? 1 : 0,
|
||||
'tpay_security_code' => $values['tpay_security_code'] ?? '',
|
||||
'piksel' => $values['piksel'] ?? '',
|
||||
'generate_webp' => $this->isEnabled($values['generate_webp'] ?? null) ? 1 : 0,
|
||||
'lazy_loading' => $this->isEnabled($values['lazy_loading'] ?? null) ? 1 : 0,
|
||||
'orlen_paczka_map_token' => $values['orlen_paczka_map_token'] ?? '',
|
||||
'google_tag_manager_id' => $values['google_tag_manager_id'] ?? '',
|
||||
'infinitescroll' => $this->isEnabled($values['infinitescroll'] ?? null) ? 1 : 0,
|
||||
'own_gtm_js' => $values['own_gtm_js'] ?? '',
|
||||
'own_gtm_html' => $values['own_gtm_html'] ?? '',
|
||||
];
|
||||
|
||||
$warehouseMessageZero = $values['warehouse_message_zero'] ?? [];
|
||||
if (is_array($warehouseMessageZero)) {
|
||||
foreach ($warehouseMessageZero as $key => $value) {
|
||||
$settingsToSave['warehouse_message_zero_' . $key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$warehouseMessageNonZero = $values['warehouse_message_nonzero'] ?? [];
|
||||
if (is_array($warehouseMessageNonZero)) {
|
||||
foreach ($warehouseMessageNonZero as $key => $value) {
|
||||
$settingsToSave['warehouse_message_nonzero_' . $key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Zachowanie zgodne z dotychczasowym flow: pelna podmiana zestawu ustawien.
|
||||
$this->db->query('TRUNCATE pp_settings');
|
||||
$this->updateSettings($settingsToSave);
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
|
||||
\Shared\Helpers\Helpers::set_message('Ustawienia zostaly zapisane');
|
||||
|
||||
return ['status' => 'ok', 'msg' => 'Ustawienia zostaly zapisane.'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualizacja pojedynczego parametru.
|
||||
*/
|
||||
public function updateSetting(string $param, $value): bool
|
||||
{
|
||||
$this->db->delete('pp_settings', ['param' => $param]);
|
||||
$this->db->insert('pp_settings', ['param' => $param, 'value' => $value]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualizacja wielu parametrow przez jedna sciezke.
|
||||
*/
|
||||
public function updateSettings(array $settings): bool
|
||||
{
|
||||
foreach ($settings as $param => $value) {
|
||||
$this->updateSetting((string)$param, $value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobranie wszystkich ustawien.
|
||||
*
|
||||
* @return array Tablica ustawien [param => value]
|
||||
*/
|
||||
public function getSettings(): array
|
||||
{
|
||||
$results = $this->db->select('pp_settings', '*', ['ORDER' => ['id' => 'ASC']]);
|
||||
$settings = [];
|
||||
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
if (isset($row['param'])) {
|
||||
$settings[$row['param']] = $row['value'] ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobranie wszystkich ustawien z cache Redis.
|
||||
*
|
||||
* @param bool $skipCache Pomija cache (np. przy generowaniu htaccess)
|
||||
*/
|
||||
public function allSettings(bool $skipCache = false): array
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'Domain\Settings\SettingsRepository::allSettings';
|
||||
|
||||
if (!$skipCache) {
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
}
|
||||
|
||||
$results = $this->db->select('pp_settings', '*');
|
||||
$settings = [];
|
||||
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$settings[$row['param']] = $row['value'];
|
||||
}
|
||||
}
|
||||
|
||||
$cacheHandler->set($cacheKey, $settings);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobranie pojedynczej wartosci ustawienia po nazwie parametru.
|
||||
*/
|
||||
public function getSingleValue(string $param): string
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = "Domain\Settings\SettingsRepository::getSingleValue:$param";
|
||||
|
||||
$objectData = $cacheHandler->get($cacheKey);
|
||||
if ($objectData) {
|
||||
return unserialize($objectData);
|
||||
}
|
||||
|
||||
$value = $this->db->get('pp_settings', 'value', ['param' => $param]);
|
||||
$value = (string)($value ?? '');
|
||||
|
||||
$cacheHandler->set($cacheKey, $value);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function isEnabled($value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return (int)$value === 1;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
181
autoload/Domain/ShopStatus/ShopStatusRepository.php
Normal file
181
autoload/Domain/ShopStatus/ShopStatusRepository.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
namespace Domain\ShopStatus;
|
||||
|
||||
class ShopStatusRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'o',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'ss.id',
|
||||
'status' => 'ss.status',
|
||||
'color' => 'ss.color',
|
||||
'o' => 'ss.o',
|
||||
'apilo_status_id' => 'ss.apilo_status_id',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'ss.o';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status !== '') {
|
||||
if (strlen($status) > 255) {
|
||||
$status = substr($status, 0, 255);
|
||||
}
|
||||
$where[] = 'ss.status LIKE :status';
|
||||
$params[':status'] = '%' . $status . '%';
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_statuses AS ss
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
ss.id,
|
||||
ss.status,
|
||||
ss.color,
|
||||
ss.o,
|
||||
ss.apilo_status_id
|
||||
FROM pp_shop_statuses AS ss
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, ss.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item['id'] = (int)($item['id'] ?? 0);
|
||||
$item['apilo_status_id'] = $item['apilo_status_id'] !== null
|
||||
? (int)$item['apilo_status_id']
|
||||
: null;
|
||||
$item['o'] = (int)($item['o'] ?? 0);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $statusId): ?array
|
||||
{
|
||||
if ($statusId < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$status = $this->db->get('pp_shop_statuses', '*', ['id' => $statusId]);
|
||||
if (!is_array($status)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$status['id'] = (int)($status['id'] ?? 0);
|
||||
$status['apilo_status_id'] = $status['apilo_status_id'] !== null
|
||||
? (int)$status['apilo_status_id']
|
||||
: null;
|
||||
$status['o'] = (int)($status['o'] ?? 0);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function save(int $statusId, array $data): int
|
||||
{
|
||||
if ($statusId < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'color' => trim((string)($data['color'] ?? '')),
|
||||
'apilo_status_id' => isset($data['apilo_status_id']) && $data['apilo_status_id'] !== ''
|
||||
? (int)$data['apilo_status_id']
|
||||
: null,
|
||||
];
|
||||
|
||||
$this->db->update('pp_shop_statuses', $row, ['id' => $statusId]);
|
||||
|
||||
return $statusId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera Apilo status ID dla danego statusu sklepowego.
|
||||
* Odpowiednik front\factory\ShopStatuses::get_apilo_status_id()
|
||||
*/
|
||||
public function getApiloStatusId(int $statusId): ?int
|
||||
{
|
||||
$value = $this->db->get('pp_shop_statuses', 'apilo_status_id', ['id' => $statusId]);
|
||||
return $value !== null && $value !== false ? (int)$value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera shop status ID na podstawie ID statusu integracji.
|
||||
* Odpowiednik front\factory\ShopStatuses::get_shop_status_by_integration_status_id()
|
||||
*/
|
||||
public function getByIntegrationStatusId(string $integration, int $integrationStatusId): ?int
|
||||
{
|
||||
if ($integration === 'apilo') {
|
||||
$value = $this->db->get('pp_shop_statuses', 'id', [
|
||||
'apilo_status_id' => $integrationStatusId,
|
||||
]);
|
||||
return $value !== null && $value !== false ? (int)$value : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca liste wszystkich statusow (id => nazwa) posortowanych wg kolejnosci.
|
||||
* Odpowiednik shop\Order::order_statuses()
|
||||
*/
|
||||
public function allStatuses(): array
|
||||
{
|
||||
$results = $this->db->select('pp_shop_statuses', ['id', 'status'], [
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
$statuses = [];
|
||||
if (is_array($results)) {
|
||||
foreach ($results as $row) {
|
||||
$statuses[(int)$row['id']] = $row['status'];
|
||||
}
|
||||
}
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
}
|
||||
471
autoload/Domain/Transport/TransportRepository.php
Normal file
471
autoload/Domain/Transport/TransportRepository.php
Normal file
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
namespace Domain\Transport;
|
||||
|
||||
class TransportRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function listForAdmin(
|
||||
array $filters = [],
|
||||
string $sortColumn = 'name',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'id' => 'st.id',
|
||||
'name' => 'st.name',
|
||||
'status' => 'st.status',
|
||||
'cost' => 'st.cost',
|
||||
'max_wp' => 'st.max_wp',
|
||||
'default' => 'st.default',
|
||||
'o' => 'st.o',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'st.name';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['1 = 1'];
|
||||
$params = [];
|
||||
|
||||
$name = trim((string)($filters['name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
if (strlen($name) > 255) {
|
||||
$name = substr($name, 0, 255);
|
||||
}
|
||||
$where[] = 'st.name LIKE :name';
|
||||
$params[':name'] = '%' . $name . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'st.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_shop_transports AS st
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
st.id,
|
||||
st.name,
|
||||
st.name_visible,
|
||||
st.description,
|
||||
st.status,
|
||||
st.cost,
|
||||
st.max_wp,
|
||||
st.default,
|
||||
st.apilo_carrier_account_id,
|
||||
st.delivery_free,
|
||||
st.o
|
||||
FROM pp_shop_transports AS st
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, st.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
foreach ($items as &$item) {
|
||||
$item = $this->normalizeTransport($item);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return [
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function find(int $transportId): ?array
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->db->get('pp_shop_transports', '*', ['id' => $transportId]);
|
||||
|
||||
if (!is_array($transport)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
|
||||
$paymentMethods = $this->db->select(
|
||||
'pp_shop_transport_payment_methods',
|
||||
'id_payment_method',
|
||||
['id_transport' => $transportId]
|
||||
);
|
||||
|
||||
$transport['payment_methods'] = is_array($paymentMethods) ? $paymentMethods : [];
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
public function save(array $data): ?int
|
||||
{
|
||||
$transportId = isset($data['id']) ? (int)$data['id'] : 0;
|
||||
$name = trim((string)($data['name'] ?? ''));
|
||||
$nameVisible = trim((string)($data['name_visible'] ?? ''));
|
||||
$description = trim((string)($data['description'] ?? ''));
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$cost = isset($data['cost']) ? (float)$data['cost'] : 0.0;
|
||||
$maxWp = isset($data['max_wp']) && $data['max_wp'] !== '' ? (int)$data['max_wp'] : null;
|
||||
$default = $this->toSwitchValue($data['default'] ?? 0);
|
||||
$apiloCarrierAccountId = isset($data['apilo_carrier_account_id']) && $data['apilo_carrier_account_id'] !== ''
|
||||
? (int)$data['apilo_carrier_account_id']
|
||||
: null;
|
||||
$deliveryFree = $this->toSwitchValue($data['delivery_free'] ?? 0);
|
||||
$paymentMethods = $data['payment_methods'] ?? [];
|
||||
|
||||
if ($default === 1) {
|
||||
$this->db->update('pp_shop_transports', ['default' => 0]);
|
||||
}
|
||||
|
||||
$transportData = [
|
||||
'name' => $name,
|
||||
'name_visible' => $nameVisible,
|
||||
'description' => $description,
|
||||
'status' => $status,
|
||||
'default' => $default,
|
||||
'cost' => $cost,
|
||||
'max_wp' => $maxWp,
|
||||
'apilo_carrier_account_id' => $apiloCarrierAccountId,
|
||||
'delivery_free' => $deliveryFree,
|
||||
];
|
||||
|
||||
if (!$transportId) {
|
||||
$this->db->insert('pp_shop_transports', $transportData);
|
||||
$id = $this->db->id();
|
||||
|
||||
if ($id) {
|
||||
$this->savePaymentMethodLinks((int)$id, $paymentMethods);
|
||||
return (int)$id;
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
$this->db->update('pp_shop_transports', $transportData, ['id' => $transportId]);
|
||||
$this->db->delete('pp_shop_transport_payment_methods', ['id_transport' => $transportId]);
|
||||
$this->savePaymentMethodLinks($transportId, $paymentMethods);
|
||||
return $transportId;
|
||||
}
|
||||
}
|
||||
|
||||
public function allActive(): array
|
||||
{
|
||||
$transports = $this->db->select(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
[
|
||||
'status' => 1,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]
|
||||
);
|
||||
|
||||
if (!is_array($transports)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($transports as &$transport) {
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
}
|
||||
unset($transport);
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
public function findActiveById(int $transportId): ?array
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$transport = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
['AND' => ['id' => $transportId, 'status' => 1]]
|
||||
);
|
||||
|
||||
if (!$transport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->normalizeTransport($transport);
|
||||
}
|
||||
|
||||
public function getApiloCarrierAccountId(int $transportId): ?int
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'apilo_carrier_account_id',
|
||||
['id' => $transportId]
|
||||
);
|
||||
|
||||
return $result !== null ? (int)$result : null;
|
||||
}
|
||||
|
||||
public function getTransportCost(int $transportId): ?float
|
||||
{
|
||||
if ($transportId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'cost',
|
||||
['AND' => ['id' => $transportId, 'status' => 1]]
|
||||
);
|
||||
|
||||
return $result !== null ? (float)$result : null;
|
||||
}
|
||||
|
||||
public function lowestTransportPrice(int $wp): ?float
|
||||
{
|
||||
$result = $this->db->get(
|
||||
'pp_shop_transports',
|
||||
'cost',
|
||||
[
|
||||
'AND' => [
|
||||
'status' => 1,
|
||||
'id' => [2, 4, 6, 8, 9],
|
||||
'max_wp[>=]' => $wp
|
||||
],
|
||||
'ORDER' => ['cost' => 'ASC']
|
||||
]
|
||||
);
|
||||
|
||||
return $result !== null ? (float)$result : null;
|
||||
}
|
||||
|
||||
public function allForAdmin(): array
|
||||
{
|
||||
$transports = $this->db->select(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
['ORDER' => ['o' => 'ASC']]
|
||||
);
|
||||
|
||||
if (!is_array($transports)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($transports as &$transport) {
|
||||
$transport = $this->normalizeTransport($transport);
|
||||
}
|
||||
unset($transport);
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Frontend methods (migrated from front\factory\ShopTransport)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Lista metod transportu dla koszyka (z filtrowaniem wagi + darmowa dostawa)
|
||||
*/
|
||||
public function transportMethodsFront( $basket, $coupon ): array
|
||||
{
|
||||
global $settings;
|
||||
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'transport_methods_front';
|
||||
$cached = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( $cached )
|
||||
{
|
||||
$transports_tmp = unserialize( $cached );
|
||||
}
|
||||
else
|
||||
{
|
||||
$transports_tmp = $this->allActive();
|
||||
$cacheHandler->set( $cacheKey, $transports_tmp );
|
||||
}
|
||||
|
||||
$wp_summary = \Domain\Basket\BasketCalculator::summaryWp( $basket );
|
||||
|
||||
$transports = [];
|
||||
foreach ( $transports_tmp as $tr )
|
||||
{
|
||||
if ( $tr['max_wp'] == null )
|
||||
$transports[] = $tr;
|
||||
elseif ( $tr['max_wp'] != null and $wp_summary <= $tr['max_wp'] )
|
||||
$transports[] = $tr;
|
||||
}
|
||||
|
||||
if ( \Shared\Helpers\Helpers::normalize_decimal( \Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon ) ) >= \Shared\Helpers\Helpers::normalize_decimal( $settings['free_delivery'] ) )
|
||||
{
|
||||
for ( $i = 0; $i < count( $transports ); $i++ ) {
|
||||
if ( $transports[$i]['delivery_free'] == 1 ) {
|
||||
$transports[$i]['cost'] = 0.00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Koszt transportu z cache
|
||||
*/
|
||||
public function transportCostCached( $transportId )
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'transport_cost_' . $transportId;
|
||||
$cached = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( $cached )
|
||||
{
|
||||
return unserialize( $cached );
|
||||
}
|
||||
|
||||
$cost = $this->getTransportCost( (int)$transportId );
|
||||
$cacheHandler->set( $cacheKey, $cost );
|
||||
|
||||
return $cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktywny transport z cache
|
||||
*/
|
||||
public function findActiveByIdCached( $transportId )
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
$cacheKey = 'transport' . $transportId;
|
||||
$cached = $cacheHandler->get( $cacheKey );
|
||||
|
||||
if ( $cached )
|
||||
{
|
||||
return unserialize( $cached );
|
||||
}
|
||||
|
||||
$transport = $this->findActiveById( (int)$transportId );
|
||||
$cacheHandler->set( $cacheKey, $transport );
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transporty powiązane z metodą płatności
|
||||
*/
|
||||
public function forPaymentMethod( int $paymentMethodId ): array
|
||||
{
|
||||
if ( $paymentMethodId <= 0 )
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$transportIds = $this->db->select(
|
||||
'pp_shop_transport_payment_methods',
|
||||
'id_transport',
|
||||
['id_payment_method' => $paymentMethodId]
|
||||
);
|
||||
|
||||
if ( !is_array( $transportIds ) || empty( $transportIds ) )
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$transports = $this->db->select(
|
||||
'pp_shop_transports',
|
||||
'*',
|
||||
['AND' => ['id' => $transportIds, 'status' => 1], 'ORDER' => ['o' => 'ASC']]
|
||||
);
|
||||
|
||||
if ( !is_array( $transports ) )
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ( $transports as &$transport )
|
||||
{
|
||||
$transport = $this->normalizeTransport( $transport );
|
||||
}
|
||||
unset( $transport );
|
||||
|
||||
return $transports;
|
||||
}
|
||||
|
||||
private function savePaymentMethodLinks(int $transportId, $paymentMethods): void
|
||||
{
|
||||
if (is_array($paymentMethods)) {
|
||||
foreach ($paymentMethods as $paymentMethodId) {
|
||||
$this->db->insert('pp_shop_transport_payment_methods', [
|
||||
'id_payment_method' => (int)$paymentMethodId,
|
||||
'id_transport' => $transportId,
|
||||
]);
|
||||
}
|
||||
} elseif ($paymentMethods) {
|
||||
$this->db->insert('pp_shop_transport_payment_methods', [
|
||||
'id_payment_method' => (int)$paymentMethods,
|
||||
'id_transport' => $transportId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function normalizeTransport(array $transport): array
|
||||
{
|
||||
$transport['id'] = isset($transport['id']) ? (int)$transport['id'] : 0;
|
||||
$transport['status'] = $this->toSwitchValue($transport['status'] ?? 0);
|
||||
$transport['default'] = $this->toSwitchValue($transport['default'] ?? 0);
|
||||
$transport['delivery_free'] = $this->toSwitchValue($transport['delivery_free'] ?? 0);
|
||||
$transport['cost'] = isset($transport['cost']) ? (float)$transport['cost'] : 0.0;
|
||||
$transport['max_wp'] = isset($transport['max_wp']) && $transport['max_wp'] !== null
|
||||
? (int)$transport['max_wp']
|
||||
: null;
|
||||
$transport['apilo_carrier_account_id'] = isset($transport['apilo_carrier_account_id']) && $transport['apilo_carrier_account_id'] !== null
|
||||
? (int)$transport['apilo_carrier_account_id']
|
||||
: null;
|
||||
$transport['o'] = isset($transport['o']) ? (int)$transport['o'] : 0;
|
||||
|
||||
return $transport;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
319
autoload/Domain/Update/UpdateRepository.php
Normal file
319
autoload/Domain/Update/UpdateRepository.php
Normal file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
namespace Domain\Update;
|
||||
|
||||
class UpdateRepository
|
||||
{
|
||||
private $db;
|
||||
|
||||
public function __construct( $db )
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wykonuje aktualizację do następnej wersji.
|
||||
*
|
||||
* @return array{success: bool, log: array, no_updates?: bool}
|
||||
*/
|
||||
public function update(): array
|
||||
{
|
||||
global $settings;
|
||||
|
||||
@file_put_contents( '../libraries/update_log.txt', '' );
|
||||
|
||||
$log = [];
|
||||
$log[] = '[START] Rozpoczęcie aktualizacji - ' . date( 'Y-m-d H:i:s' );
|
||||
$log[] = '[INFO] Aktualna wersja: ' . \Shared\Helpers\Helpers::get_version();
|
||||
|
||||
\Shared\Helpers\Helpers::delete_session( 'new-version' );
|
||||
|
||||
$versionsUrl = 'https://shoppro.project-dc.pl/updates/versions.php?key=' . $settings['update_key'];
|
||||
$versions = @file_get_contents( $versionsUrl );
|
||||
|
||||
if ( $versions === false ) {
|
||||
$log[] = '[ERROR] Nie udało się pobrać listy wersji z: ' . $versionsUrl;
|
||||
$this->saveLog( $log );
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
|
||||
$log[] = '[OK] Pobrano listę wersji';
|
||||
$versions = explode( PHP_EOL, $versions );
|
||||
$log[] = '[INFO] Znaleziono ' . count( $versions ) . ' wersji do sprawdzenia';
|
||||
|
||||
foreach ( $versions as $ver ) {
|
||||
$ver = trim( $ver );
|
||||
if ( floatval( $ver ) <= (float) \Shared\Helpers\Helpers::get_version() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$log[] = '[INFO] Aktualizacja do wersji: ' . $ver;
|
||||
$dir = strlen( $ver ) == 5
|
||||
? substr( $ver, 0, strlen( $ver ) - 2 ) . '0'
|
||||
: substr( $ver, 0, strlen( $ver ) - 1 ) . '0';
|
||||
|
||||
$result = $this->downloadAndApply( $ver, $dir, $log );
|
||||
$this->saveLog( $result['log'] );
|
||||
return $result;
|
||||
}
|
||||
|
||||
$log[] = '[INFO] Brak nowych wersji do zainstalowania';
|
||||
$this->saveLog( $log );
|
||||
return [ 'success' => true, 'log' => $log, 'no_updates' => true ];
|
||||
}
|
||||
|
||||
private function downloadAndApply( string $ver, string $dir, array $log ): array
|
||||
{
|
||||
$baseUrl = 'https://shoppro.project-dc.pl/updates/' . $dir;
|
||||
|
||||
// Pobieranie ZIP
|
||||
$zipUrl = $baseUrl . '/ver_' . $ver . '.zip';
|
||||
$log[] = '[INFO] Pobieranie pliku ZIP: ' . $zipUrl;
|
||||
$file = @file_get_contents( $zipUrl );
|
||||
|
||||
if ( $file === false ) {
|
||||
$log[] = '[ERROR] Nie udało się pobrać pliku ZIP';
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
|
||||
$fileSize = strlen( $file );
|
||||
$log[] = '[OK] Pobrano plik ZIP, rozmiar: ' . $fileSize . ' bajtów';
|
||||
|
||||
if ( $fileSize < 100 ) {
|
||||
$log[] = '[ERROR] Plik ZIP jest za mały (prawdopodobnie błąd pobierania)';
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
|
||||
$dlHandler = @fopen( 'update.zip', 'w' );
|
||||
if ( !$dlHandler ) {
|
||||
$log[] = '[ERROR] Nie udało się otworzyć pliku update.zip do zapisu';
|
||||
$log[] = '[INFO] Katalog roboczy: ' . getcwd();
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
|
||||
$written = fwrite( $dlHandler, $file );
|
||||
fclose( $dlHandler );
|
||||
|
||||
if ( $written === false || $written === 0 ) {
|
||||
$log[] = '[ERROR] Nie udało się zapisać pliku ZIP';
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
|
||||
$log[] = '[OK] Zapisano plik ZIP (' . $written . ' bajtów)';
|
||||
|
||||
// Wykonanie SQL
|
||||
$log = $this->executeSql( $baseUrl . '/ver_' . $ver . '_sql.txt', $log );
|
||||
|
||||
// Usuwanie plików
|
||||
$log = $this->deleteFiles( $baseUrl . '/ver_' . $ver . '_files.txt', $log );
|
||||
|
||||
// Rozpakowywanie ZIP
|
||||
$log = $this->extractZip( 'update.zip', $log );
|
||||
|
||||
// Aktualizacja wersji
|
||||
$versionFile = '../libraries/version.ini';
|
||||
$handle = @fopen( $versionFile, 'w' );
|
||||
if ( !$handle ) {
|
||||
$log[] = '[ERROR] Nie udało się otworzyć pliku version.ini do zapisu';
|
||||
return [ 'success' => false, 'log' => $log ];
|
||||
}
|
||||
fwrite( $handle, $ver );
|
||||
fclose( $handle );
|
||||
|
||||
$log[] = '[OK] Zaktualizowano plik version.ini do wersji: ' . $ver;
|
||||
$log[] = '[SUCCESS] Aktualizacja do wersji ' . $ver . ' zakończona pomyślnie';
|
||||
|
||||
return [ 'success' => true, 'log' => $log ];
|
||||
}
|
||||
|
||||
private function executeSql( string $sqlUrl, array $log ): array
|
||||
{
|
||||
$log[] = '[INFO] Sprawdzanie aktualizacji SQL: ' . $sqlUrl;
|
||||
|
||||
$ch = curl_init( $sqlUrl );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HEADER, false );
|
||||
$response = curl_exec( $ch );
|
||||
$contentType = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
|
||||
$httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
if ( !$response || strpos( $contentType, 'text/plain' ) === false ) {
|
||||
$log[] = '[INFO] Brak aktualizacji SQL (HTTP: ' . $httpCode . ')';
|
||||
return $log;
|
||||
}
|
||||
|
||||
$queries = explode( PHP_EOL, $response );
|
||||
$log[] = '[OK] Pobrano ' . count( $queries ) . ' zapytań SQL';
|
||||
$success = 0;
|
||||
$errors = 0;
|
||||
|
||||
foreach ( $queries as $query ) {
|
||||
$query = trim( $query );
|
||||
if ( $query !== '' ) {
|
||||
if ( $this->db->query( $query ) ) {
|
||||
$success++;
|
||||
} else {
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$log[] = '[INFO] Wykonano zapytania SQL - sukces: ' . $success . ', błędy: ' . $errors;
|
||||
return $log;
|
||||
}
|
||||
|
||||
private function deleteFiles( string $filesUrl, array $log ): array
|
||||
{
|
||||
$log[] = '[INFO] Sprawdzanie plików do usunięcia: ' . $filesUrl;
|
||||
|
||||
$ch = curl_init( $filesUrl );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HEADER, false );
|
||||
$response = curl_exec( $ch );
|
||||
$contentType = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
|
||||
curl_close( $ch );
|
||||
|
||||
if ( !$response || strpos( $contentType, 'text/plain' ) === false ) {
|
||||
$log[] = '[INFO] Brak plików do usunięcia';
|
||||
return $log;
|
||||
}
|
||||
|
||||
$files = explode( PHP_EOL, $response );
|
||||
$deletedFiles = 0;
|
||||
$deletedDirs = 0;
|
||||
|
||||
foreach ( $files as $entry ) {
|
||||
if ( strpos( $entry, 'F: ' ) !== false ) {
|
||||
$path = substr( $entry, 3 );
|
||||
if ( file_exists( $path ) ) {
|
||||
if ( @unlink( $path ) ) {
|
||||
$deletedFiles++;
|
||||
} else {
|
||||
$log[] = '[WARNING] Nie udało się usunąć pliku: ' . $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( strpos( $entry, 'D: ' ) !== false ) {
|
||||
$path = substr( $entry, 3 );
|
||||
if ( is_dir( $path ) ) {
|
||||
\Shared\Helpers\Helpers::delete_dir( $path );
|
||||
$deletedDirs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$log[] = '[INFO] Usunięto plików: ' . $deletedFiles . ', katalogów: ' . $deletedDirs;
|
||||
return $log;
|
||||
}
|
||||
|
||||
private function extractZip( string $fileName, array $log ): array
|
||||
{
|
||||
$log[] = '[INFO] Rozpoczęcie rozpakowywania pliku ZIP';
|
||||
|
||||
$path = pathinfo( realpath( $fileName ), PATHINFO_DIRNAME );
|
||||
$path = substr( $path, 0, strlen( $path ) - 5 );
|
||||
|
||||
if ( !is_dir( $path ) || !is_writable( $path ) ) {
|
||||
$log[] = '[ERROR] Ścieżka docelowa nie istnieje lub brak uprawnień: ' . $path;
|
||||
return $log;
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
$res = $zip->open( $fileName );
|
||||
|
||||
if ( $res !== true ) {
|
||||
$log[] = '[ERROR] Nie udało się otworzyć pliku ZIP (kod: ' . $res . ')';
|
||||
return $log;
|
||||
}
|
||||
|
||||
$log[] = '[OK] Otwarto archiwum ZIP, liczba plików: ' . $zip->numFiles;
|
||||
$extracted = 0;
|
||||
$errors = 0;
|
||||
|
||||
for ( $i = 0; $i < $zip->numFiles; $i++ ) {
|
||||
$filename = str_replace( '\\', '/', $zip->getNameIndex( $i ) );
|
||||
|
||||
if ( substr( $filename, -1 ) === '/' ) {
|
||||
$dirPath = $path . '/' . $filename;
|
||||
if ( !is_dir( $dirPath ) ) {
|
||||
@mkdir( $dirPath, 0755, true );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetFile = $path . '/' . $filename;
|
||||
$targetDir = dirname( $targetFile );
|
||||
|
||||
if ( !is_dir( $targetDir ) ) {
|
||||
@mkdir( $targetDir, 0755, true );
|
||||
}
|
||||
|
||||
$existed = file_exists( $targetFile );
|
||||
$content = $zip->getFromIndex( $i );
|
||||
|
||||
if ( $content === false ) {
|
||||
$log[] = '[ERROR] Nie udało się odczytać z ZIP: ' . $filename;
|
||||
$errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( @file_put_contents( $targetFile, $content ) === false ) {
|
||||
$log[] = '[ERROR] Nie udało się zapisać: ' . $filename;
|
||||
$errors++;
|
||||
} else {
|
||||
$tag = $existed ? '[UPDATED]' : '[NEW]';
|
||||
$log[] = $tag . ' ' . $filename . ' (' . strlen( $content ) . ' bajtów)';
|
||||
$extracted++;
|
||||
}
|
||||
}
|
||||
|
||||
$log[] = '[OK] Rozpakowano ' . $extracted . ' plików, błędów: ' . $errors;
|
||||
$zip->close();
|
||||
|
||||
if ( @unlink( $fileName ) ) {
|
||||
$log[] = '[OK] Usunięto plik update.zip';
|
||||
}
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
private function saveLog( array $log ): void
|
||||
{
|
||||
@file_put_contents( '../libraries/update_log.txt', implode( "\n", $log ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wykonuje zaległe migracje z tabeli pp_updates.
|
||||
*/
|
||||
public function runPendingMigrations(): void
|
||||
{
|
||||
$results = $this->db->select( 'pp_updates', [ 'name' ], [ 'done' => 0 ] );
|
||||
if ( !is_array( $results ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $results as $row ) {
|
||||
$method = $row['name'];
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
$this->$method();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function update0197(): void
|
||||
{
|
||||
$rows = $this->db->select( 'pp_shop_order_products', [ 'id', 'product_id' ], [ 'parent_product_id' => null ] );
|
||||
|
||||
if ( is_array( $rows ) ) {
|
||||
foreach ( $rows as $row ) {
|
||||
$parentId = $this->db->get( 'pp_shop_products', 'parent_id', [ 'id' => $row['product_id'] ] );
|
||||
$this->db->update( 'pp_shop_order_products', [
|
||||
'parent_product_id' => $parentId ?: $row['product_id'],
|
||||
], [ 'id' => $row['id'] ] );
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->update( 'pp_updates', [ 'done' => 1 ], [ 'name' => 'update0197' ] );
|
||||
}
|
||||
}
|
||||
339
autoload/Domain/User/UserRepository.php
Normal file
339
autoload/Domain/User/UserRepository.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
namespace Domain\User;
|
||||
|
||||
/**
|
||||
* Repository odpowiedzialny za dostep do danych uzytkownikow admina.
|
||||
*/
|
||||
class UserRepository
|
||||
{
|
||||
private const MAX_PER_PAGE = 100;
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getById(int $userId): ?array
|
||||
{
|
||||
$user = $this->db->get('pp_users', '*', ['id' => $userId]);
|
||||
return $user ?: null;
|
||||
}
|
||||
|
||||
public function updateById(int $userId, array $data): bool
|
||||
{
|
||||
return (bool)$this->db->update('pp_users', $data, ['id' => $userId]);
|
||||
}
|
||||
|
||||
public function verifyTwofaCode(int $userId, string $code): bool
|
||||
{
|
||||
$user = $this->getById($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)($user['twofa_failed_attempts'] ?? 0) >= 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($user['twofa_expires_at']) || time() > strtotime((string)$user['twofa_expires_at'])) {
|
||||
$this->updateById($userId, [
|
||||
'twofa_code_hash' => null,
|
||||
'twofa_expires_at' => null,
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$ok = (!empty($user['twofa_code_hash']) && password_verify($code, (string)$user['twofa_code_hash']));
|
||||
if ($ok) {
|
||||
$this->updateById($userId, [
|
||||
'twofa_code_hash' => null,
|
||||
'twofa_expires_at' => null,
|
||||
'twofa_sent_at' => null,
|
||||
'twofa_failed_attempts' => 0,
|
||||
'last_logged' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->updateById($userId, [
|
||||
'twofa_failed_attempts' => (int)($user['twofa_failed_attempts'] ?? 0) + 1,
|
||||
'last_error_logged' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function sendTwofaCode(int $userId, bool $resend = false): bool
|
||||
{
|
||||
$user = $this->getById($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)($user['twofa_enabled'] ?? 0) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$to = !empty($user['twofa_email']) ? (string)$user['twofa_email'] : (string)$user['login'];
|
||||
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($resend && !empty($user['twofa_sent_at'])) {
|
||||
$last = strtotime((string)$user['twofa_sent_at']);
|
||||
if ($last && (time() - $last) < 30) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$code = random_int(100000, 999999);
|
||||
$hash = password_hash((string)$code, PASSWORD_DEFAULT);
|
||||
|
||||
$this->updateById($userId, [
|
||||
'twofa_code_hash' => $hash,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 10 * 60),
|
||||
'twofa_sent_at' => date('Y-m-d H:i:s'),
|
||||
'twofa_failed_attempts' => 0,
|
||||
]);
|
||||
|
||||
$subject = 'Twoj kod logowania 2FA';
|
||||
$body = 'Twoj kod logowania do panelu administratora: ' . $code . '. Kod jest wazny przez 10 minut.';
|
||||
|
||||
$sent = \Shared\Helpers\Helpers::send_email($to, $subject, $body);
|
||||
if ($sent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$headers = "MIME-Version: 1.0\r\n";
|
||||
$headers .= "Content-type: text/plain; charset=UTF-8\r\n";
|
||||
$headers .= "From: no-reply@" . ($_SERVER['HTTP_HOST'] ?? 'localhost') . "\r\n";
|
||||
$encodedSubject = mb_encode_mimeheader($subject, 'UTF-8');
|
||||
|
||||
return mail($to, $encodedSubject, $body, $headers);
|
||||
}
|
||||
|
||||
public function delete(int $userId): bool
|
||||
{
|
||||
return (bool)$this->db->delete('pp_users', ['id' => $userId]);
|
||||
}
|
||||
|
||||
public function find(int $userId): ?array
|
||||
{
|
||||
$user = $this->db->get('pp_users', '*', ['id' => $userId]);
|
||||
return $user ?: null;
|
||||
}
|
||||
|
||||
public function save(
|
||||
int $userId,
|
||||
string $login,
|
||||
$status,
|
||||
string $password,
|
||||
string $passwordRepeat,
|
||||
$admin,
|
||||
$twofaEnabled = 0,
|
||||
string $twofaEmail = ''
|
||||
): array {
|
||||
if ($userId <= 0) {
|
||||
if (strlen($password) < 5) {
|
||||
return ['status' => 'error', 'msg' => 'Podane haslo jest zbyt krotkie.'];
|
||||
}
|
||||
|
||||
if ($password !== $passwordRepeat) {
|
||||
return ['status' => 'error', 'msg' => 'Podane hasla sa rozne'];
|
||||
}
|
||||
|
||||
$inserted = $this->db->insert('pp_users', [
|
||||
'login' => $login,
|
||||
'status' => $this->toSwitchValue($status),
|
||||
'admin' => (int)$admin,
|
||||
'password' => md5($password),
|
||||
'twofa_enabled' => $this->toSwitchValue($twofaEnabled),
|
||||
'twofa_email' => $twofaEmail,
|
||||
]);
|
||||
|
||||
if ($inserted) {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return ['status' => 'ok', 'msg' => 'Uzytkownik zostal zapisany.'];
|
||||
}
|
||||
|
||||
return ['status' => 'error', 'msg' => 'Podczas zapisywania uzytkownika wystapil blad.'];
|
||||
}
|
||||
|
||||
if ($password !== '' && strlen($password) < 5) {
|
||||
return ['status' => 'error', 'msg' => 'Podane haslo jest zbyt krotkie.'];
|
||||
}
|
||||
|
||||
if ($password !== '' && $password !== $passwordRepeat) {
|
||||
return ['status' => 'error', 'msg' => 'Podane hasla sa rozne'];
|
||||
}
|
||||
|
||||
if ($password !== '') {
|
||||
$this->db->update('pp_users', [
|
||||
'password' => md5($password),
|
||||
], [
|
||||
'id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->update('pp_users', [
|
||||
'login' => $login,
|
||||
'admin' => (int)$admin,
|
||||
'status' => $this->toSwitchValue($status),
|
||||
'twofa_enabled' => $this->toSwitchValue($twofaEnabled),
|
||||
'twofa_email' => $twofaEmail,
|
||||
], [
|
||||
'id' => $userId,
|
||||
]);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
return ['status' => 'ok', 'msg' => 'Uzytkownik zostal zapisany.'];
|
||||
}
|
||||
|
||||
public function checkLogin(string $login, int $userId): array
|
||||
{
|
||||
$existing = $this->db->get('pp_users', 'login', [
|
||||
'AND' => [
|
||||
'login' => $login,
|
||||
'id[!]' => $userId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($existing) {
|
||||
return ['status' => 'error', 'msg' => 'Podany login jest juz zajety.'];
|
||||
}
|
||||
|
||||
return ['status' => 'ok'];
|
||||
}
|
||||
|
||||
public function logon(string $login, string $password): int
|
||||
{
|
||||
if (!$this->db->get('pp_users', '*', ['login' => $login])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$this->db->get('pp_users', '*', [
|
||||
'AND' => [
|
||||
'login' => $login,
|
||||
'status' => 1,
|
||||
'error_logged_count[<]' => 5,
|
||||
],
|
||||
])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($this->db->get('pp_users', '*', [
|
||||
'AND' => [
|
||||
'login' => $login,
|
||||
'status' => 1,
|
||||
'password' => md5($password),
|
||||
],
|
||||
])) {
|
||||
$this->db->update('pp_users', [
|
||||
'last_logged' => date('Y-m-d H:i:s'),
|
||||
'error_logged_count' => 0,
|
||||
], [
|
||||
'login' => $login,
|
||||
]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->db->update('pp_users', [
|
||||
'last_error_logged' => date('Y-m-d H:i:s'),
|
||||
'error_logged_count[+]' => 1,
|
||||
], [
|
||||
'login' => $login,
|
||||
]);
|
||||
|
||||
if ((int)$this->db->get('pp_users', 'error_logged_count', ['login' => $login]) >= 5) {
|
||||
$this->db->update('pp_users', ['status' => 0], ['login' => $login]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function details(string $login): ?array
|
||||
{
|
||||
$user = $this->db->get('pp_users', '*', ['login' => $login]);
|
||||
return $user ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{items: array<int, array<string, mixed>>, total: int}
|
||||
*/
|
||||
public function listForAdmin(
|
||||
array $filters,
|
||||
string $sortColumn = 'login',
|
||||
string $sortDir = 'ASC',
|
||||
int $page = 1,
|
||||
int $perPage = 15
|
||||
): array {
|
||||
$allowedSortColumns = [
|
||||
'login' => 'pu.login',
|
||||
'status' => 'pu.status',
|
||||
];
|
||||
|
||||
$sortSql = $allowedSortColumns[$sortColumn] ?? 'pu.login';
|
||||
$sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
|
||||
$page = max(1, $page);
|
||||
$perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$where = ['pu.id != 1'];
|
||||
$params = [];
|
||||
|
||||
$login = trim((string)($filters['login'] ?? ''));
|
||||
if ($login !== '') {
|
||||
if (strlen($login) > 255) {
|
||||
$login = substr($login, 0, 255);
|
||||
}
|
||||
$where[] = 'pu.login LIKE :login';
|
||||
$params[':login'] = '%' . $login . '%';
|
||||
}
|
||||
|
||||
$status = trim((string)($filters['status'] ?? ''));
|
||||
if ($status === '0' || $status === '1') {
|
||||
$where[] = 'pu.status = :status';
|
||||
$params[':status'] = (int)$status;
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $where);
|
||||
|
||||
$sqlCount = "
|
||||
SELECT COUNT(0)
|
||||
FROM pp_users AS pu
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
pu.id,
|
||||
pu.login,
|
||||
pu.status
|
||||
FROM pp_users AS pu
|
||||
WHERE {$whereSql}
|
||||
ORDER BY {$sortSql} {$sortDir}, pu.id ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
";
|
||||
|
||||
$stmt = $this->db->query($sql, $params);
|
||||
$items = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return [
|
||||
'items' => is_array($items) ? $items : [],
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
return ($value === 'on' || $value === 1 || $value === '1' || $value === true) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
<?
|
||||
<?php
|
||||
namespace Shared\Cache;
|
||||
|
||||
class CacheHandler
|
||||
{
|
||||
protected $redis;
|
||||
@@ -7,7 +9,7 @@ class CacheHandler
|
||||
{
|
||||
if (class_exists('Redis')) {
|
||||
try {
|
||||
$this->redis = \RedisConnection::getInstance()->getConnection();
|
||||
$this->redis = RedisConnection::getInstance()->getConnection();
|
||||
} catch (\Exception $e) {
|
||||
$this->redis = null;
|
||||
}
|
||||
@@ -22,7 +24,7 @@ class CacheHandler
|
||||
return null;
|
||||
}
|
||||
|
||||
public function set($key, $value, $ttl = 86400) // 86400 = 60 * 60 * 24 (1 dzień)
|
||||
public function set($key, $value, $ttl = 86400)
|
||||
{
|
||||
if ($this->redis) {
|
||||
$this->redis->setex($key, $ttl, serialize($value));
|
||||
@@ -36,4 +38,23 @@ class CacheHandler
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
if ($this->redis) {
|
||||
return $this->redis->del($key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deletePattern($pattern)
|
||||
{
|
||||
if ($this->redis) {
|
||||
$keys = $this->redis->keys($pattern);
|
||||
if (!empty($keys)) {
|
||||
return $this->redis->del($keys);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
45
autoload/Shared/Cache/RedisConnection.php
Normal file
45
autoload/Shared/Cache/RedisConnection.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace Shared\Cache;
|
||||
|
||||
class RedisConnection
|
||||
{
|
||||
private static $instance = null;
|
||||
private $redis;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
global $config;
|
||||
|
||||
$this->redis = new \Redis();
|
||||
|
||||
try {
|
||||
if (!$this->redis->connect($config['redis']['host'], $config['redis']['port'])) {
|
||||
error_log("Nie udalo sie polaczyc z serwerem Redis.");
|
||||
$this->redis = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->redis->auth($config['redis']['password'])) {
|
||||
error_log("Autoryzacja do serwera Redis nie powiodla sie.");
|
||||
$this->redis = null;
|
||||
return;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
error_log("Blad podczas polaczenia z Redis: " . $e->getMessage());
|
||||
$this->redis = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->redis;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
class Email extends DbModel
|
||||
namespace Shared\Email;
|
||||
|
||||
class Email
|
||||
{
|
||||
public $table = 'pp_newsletter_templates';
|
||||
|
||||
@@ -76,4 +78,4 @@ class Email extends DbModel
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,80 @@
|
||||
<?php
|
||||
class S
|
||||
namespace Shared\Helpers;
|
||||
class Helpers
|
||||
{
|
||||
public static function set_array_value(&$array, $keys, $value)
|
||||
{
|
||||
$key = array_shift($keys);
|
||||
if (count($keys) == 0)
|
||||
{
|
||||
if ($key === '')
|
||||
{
|
||||
$array[] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Jeśli klucz już istnieje i jest tablicą, zamieniamy go na tablicę wartości
|
||||
if (isset($array[$key]) && !is_array($array[$key]))
|
||||
{
|
||||
$array[$key] = [$array[$key]];
|
||||
}
|
||||
if (isset($array[$key]) && is_array($array[$key]))
|
||||
{
|
||||
$array[$key][] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$array[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($key === '')
|
||||
{
|
||||
$array[] = [];
|
||||
$key = count($array) - 1;
|
||||
}
|
||||
if (!isset($array[$key]) || !is_array($array[$key]))
|
||||
{
|
||||
$array[$key] = [];
|
||||
}
|
||||
self::set_array_value($array[$key], $keys, $value);
|
||||
}
|
||||
}
|
||||
|
||||
static public function parse_name($name)
|
||||
{
|
||||
$name = rtrim($name, ']');
|
||||
$parts = explode('[', $name);
|
||||
return $parts;
|
||||
}
|
||||
|
||||
static function canAddRedirect( $from, $to )
|
||||
static function canAddRedirect( $from, $to, $lang_id = null )
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$redirects = $mdb -> select( 'pp_redirects', '*' );
|
||||
if ( !$from or !$to or $from === $to )
|
||||
return false;
|
||||
|
||||
$where = [];
|
||||
if ( null !== $lang_id )
|
||||
$where['lang_id'] = $lang_id;
|
||||
|
||||
$redirects = $mdb -> select( 'pp_redirects', [ 'from', 'to' ], $where );
|
||||
|
||||
$redirectMap = [];
|
||||
foreach ( $redirects as $redirect )
|
||||
{
|
||||
$redirectMap[$redirect['from']] = $redirect['to'];
|
||||
if ( !isset( $redirectMap[$redirect['from']] ) )
|
||||
$redirectMap[$redirect['from']] = [];
|
||||
|
||||
if ( !in_array( $redirect['to'], $redirectMap[$redirect['from']], true ) )
|
||||
$redirectMap[$redirect['from']][] = $redirect['to'];
|
||||
}
|
||||
|
||||
// Dodaj nowe przekierowanie do mapy tymczasowo
|
||||
$redirectMap[$from] = $to;
|
||||
if ( !isset( $redirectMap[$from] ) )
|
||||
$redirectMap[$from] = [];
|
||||
|
||||
if ( !in_array( $to, $redirectMap[$from], true ) )
|
||||
$redirectMap[$from][] = $to;
|
||||
|
||||
// Funkcja do sprawdzania cyklu za pomocą DFS
|
||||
$visited = [];
|
||||
$stack = [];
|
||||
|
||||
function hasCycle($current, $target, &$redirectMap, &$visited)
|
||||
$stack = [ $to ];
|
||||
while ( !empty( $stack ) )
|
||||
{
|
||||
if ($current === $target) {
|
||||
return true;
|
||||
}
|
||||
$current = array_pop( $stack );
|
||||
|
||||
if (isset($visited[$current])) {
|
||||
return false;
|
||||
}
|
||||
if ( $current === $from )
|
||||
return false;
|
||||
|
||||
if ( isset( $visited[$current] ) )
|
||||
continue;
|
||||
|
||||
$visited[$current] = true;
|
||||
|
||||
if (isset($redirectMap[$current])) {
|
||||
return hasCycle($redirectMap[$current], $target, $redirectMap, $visited);
|
||||
if ( isset( $redirectMap[$current] ) )
|
||||
{
|
||||
foreach ( $redirectMap[$current] as $next )
|
||||
if ( !isset( $visited[$next] ) )
|
||||
$stack[] = $next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sprawdź, czy istnieje ścieżka z $newTo do $newFrom
|
||||
return !hasCycle($to, $from, $redirectMap, $visited);
|
||||
return true;
|
||||
}
|
||||
|
||||
static public function clear_redis_cache()
|
||||
static public function clear_product_cache( int $product_id )
|
||||
{
|
||||
$redis = \RedisConnection::getInstance() -> getConnection();
|
||||
$redis -> flushAll();
|
||||
if ( class_exists('Redis') )
|
||||
{
|
||||
try
|
||||
{
|
||||
$cacheHandler = new \Shared\Cache\CacheHandler();
|
||||
// Wyczyść cache produktu dla wszystkich języków i permutacji
|
||||
$cacheHandler -> deletePattern( "shop\\product:$product_id:*" );
|
||||
// Wyczyść cache związane z opcjami ilościowymi
|
||||
$cacheHandler -> deletePattern( "ProductRepository::getProductPermutationQuantityOptions:v2:$product_id:*" );
|
||||
// Wyczyść cache zestawów produktów
|
||||
$cacheHandler -> deletePattern( "ProductRepository::productSetsWhenAddToBasket:$product_id" );
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
error_log("Błąd podczas czyszczenia cache produktu: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public function remove_special_chars( $string ) {
|
||||
@@ -134,8 +114,8 @@ class S
|
||||
|
||||
$img_src = implode( '/', $file_tmp );
|
||||
|
||||
$crop_w = $_GET['c_w'];
|
||||
$crop_h = $_GET['c_h'];
|
||||
$crop_w = $_GET['c_w'] ?? null;
|
||||
$crop_h = $_GET['c_h'] ?? null;
|
||||
|
||||
$img_md5 = md5( $img_src . $height . $width . $crop_h . $crop_w );
|
||||
$file = 'thumbs/' . $img_md5[0] . '/' . $img_md5[1] . '/' . $img_md5[2] . '/' . $img_md5;
|
||||
@@ -194,7 +174,7 @@ class S
|
||||
$image = new \Imagick();
|
||||
$image->readImage($file);
|
||||
|
||||
if ($file_type === 'png')
|
||||
if ($file_type === 'image/png')
|
||||
{
|
||||
$image->setImageFormat('webp');
|
||||
$image->setImageCompressionQuality($compression_quality);
|
||||
@@ -215,40 +195,12 @@ class S
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function get_domain( $url )
|
||||
{
|
||||
$parseUrl = parse_url( trim( $url ) );
|
||||
return trim( $parseUrl['host'] ? str_replace( 'www.', '', $parseUrl['host'] ) : str_replace( 'www.', '', array_shift( explode( '/', $parseUrl['path'], 2 ) ) ) );
|
||||
}
|
||||
|
||||
static public function pre_dump( $value )
|
||||
{
|
||||
echo '<pre>';
|
||||
var_dump( $value );
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
public static function escape( $value )
|
||||
{
|
||||
$return = '';
|
||||
for ( $i = 0; $i < strlen( $value ); ++$i )
|
||||
{
|
||||
$char = $value[$i];
|
||||
$ord = ord( $char );
|
||||
if ( $char !== "'" && $char !== "\"" && $char !== '\\' && $ord >= 32 && $ord <= 126 )
|
||||
$return .= $char;
|
||||
else
|
||||
$return .= '\\x' . dechex( $ord );
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
public static function delete_cache()
|
||||
{
|
||||
\S::delete_dir( '../cache/' );
|
||||
\S::delete_dir( '../temp/' );
|
||||
\S::delete_dir( '../cron/temp/' );
|
||||
\S::delete_dir( 'temp/' );
|
||||
self::delete_dir( '../cache/' );
|
||||
self::delete_dir( '../temp/' );
|
||||
self::delete_dir( '../cron/temp/' );
|
||||
self::delete_dir( 'temp/' );
|
||||
}
|
||||
|
||||
public static function pretty_date( $format, $timestamp = null )
|
||||
@@ -285,7 +237,7 @@ class S
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($array as $key => $value)
|
||||
foreach ($input as $key => $values)
|
||||
{
|
||||
if ( empty( $values ) )
|
||||
continue;
|
||||
@@ -365,58 +317,11 @@ class S
|
||||
return number_format( $val, $precision, $dec_point, $thousands_sep );
|
||||
}
|
||||
|
||||
public static function chmod_r( $path, $chmod = 0755 )
|
||||
{
|
||||
$dir = new DirectoryIterator( $path );
|
||||
foreach ( $dir as $item )
|
||||
{
|
||||
chmod( $item -> getPathname(), $chmod );
|
||||
if ( $item -> isDir() && !$item -> isDot() )
|
||||
self::chmod_r( $item -> getPathname() );
|
||||
}
|
||||
}
|
||||
|
||||
public static function rrmdir( $dir )
|
||||
{
|
||||
if ( is_dir( $dir ) )
|
||||
{
|
||||
$files = scandir( $dir );
|
||||
foreach ( $files as $file )
|
||||
if ( $file != "." && $file != ".." )
|
||||
\S::rrmdir( "$dir/$file" );
|
||||
rmdir( $dir );
|
||||
}
|
||||
else if ( file_exists( $dir ) )
|
||||
unlink( $dir );
|
||||
}
|
||||
|
||||
public static function rcopy( $src, $dst )
|
||||
{
|
||||
if ( is_dir( $src ) )
|
||||
{
|
||||
mkdir( $dst, 0755 );
|
||||
$files = scandir( $src );
|
||||
foreach ( $files as $file )
|
||||
if ( $file != "." && $file != ".." )
|
||||
\S::rcopy( "$src/$file", "$dst/$file" );
|
||||
}
|
||||
else if ( file_exists( $src ) )
|
||||
copy( $src, $dst );
|
||||
|
||||
\S::rrmdir( $src );
|
||||
}
|
||||
|
||||
public static function is_mobile()
|
||||
{
|
||||
$detect = new \Mobile_Detect;
|
||||
return $detect -> isMobile();
|
||||
}
|
||||
|
||||
public static function get_new_version()
|
||||
{
|
||||
global $settings;
|
||||
|
||||
if ( $version = \S::get_session( 'new-version' ) )
|
||||
if ( $version = self::get_session( 'new-version' ) )
|
||||
return $version;
|
||||
|
||||
$version = 0;
|
||||
@@ -429,7 +334,7 @@ class S
|
||||
$version = $version_tmp;
|
||||
}
|
||||
|
||||
\S::set_session( 'new-version', $version );
|
||||
self::set_session( 'new-version', $version );
|
||||
|
||||
return $version;
|
||||
}
|
||||
@@ -439,42 +344,6 @@ class S
|
||||
return (float) @file_get_contents( '../libraries/version.ini' );
|
||||
}
|
||||
|
||||
public static function pre( $data, $type = '' )
|
||||
{
|
||||
$data = str_replace( 'Array
|
||||
(', '', $data );
|
||||
$data = str_replace( ')', '', $data );
|
||||
|
||||
echo '<pre';
|
||||
if ( $type == 'error' )
|
||||
echo ' style="color: #cc0000;" ';
|
||||
else if ( $type == 'info' )
|
||||
echo ' style="color: #2c539e;" ';
|
||||
else
|
||||
echo ' style="color: #8fc400;" ';
|
||||
echo '>' . print_r( $data, true ) . '</pre>';
|
||||
}
|
||||
|
||||
public static function json_to_array( $json )
|
||||
{
|
||||
$values_tmp = json_decode( $json, true );
|
||||
|
||||
if ( is_array( $values_tmp ) )
|
||||
foreach ( $values_tmp as $val )
|
||||
{
|
||||
if ( isset( $values[$val['name']] ) )
|
||||
{
|
||||
if ( is_array( $values[$val['name']] ) )
|
||||
$values[$val['name']][] = $val['value'];
|
||||
else
|
||||
$values[$val['name']] = array( $values[$val['name']], $val['value'] );
|
||||
}
|
||||
else
|
||||
$values[$val['name']] = $val['value'];
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
public static function set_session( $var, $val )
|
||||
{
|
||||
$_SESSION[$var] = $val;
|
||||
@@ -540,7 +409,7 @@ class S
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$settings = \front\factory\Settings::settings_details( true );
|
||||
$settings = ( new \Domain\Settings\SettingsRepository( $mdb ) )->allSettings( true );
|
||||
|
||||
$url = preg_replace( '#^(http(s)?://)?w{3}\.#', '$1', $_SERVER['SERVER_NAME'] );
|
||||
|
||||
@@ -576,13 +445,14 @@ class S
|
||||
//
|
||||
// PRODUCENCI
|
||||
//
|
||||
$htaccess_data .= 'RewriteRule ^producenci$ index.php?module=shop_producer&action=list&layout_id=' . \front\factory\Layouts::category_default_layout() . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
$categoryDefaultLayoutId = ( new \Domain\Layouts\LayoutsRepository( $mdb ) )->categoryDefaultLayoutId();
|
||||
$htaccess_data .= 'RewriteRule ^producenci$ index.php?module=shop_producer&action=list&layout_id=' . $categoryDefaultLayoutId . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
|
||||
$rows = $mdb -> select( 'pp_shop_producer', '*', [ 'status' => 1 ] );
|
||||
if ( \S::is_array_fix( $rows ) ) foreach ( $rows as $row )
|
||||
if ( self::is_array_fix( $rows ) ) foreach ( $rows as $row )
|
||||
{
|
||||
$htaccess_data .= 'RewriteRule ^producent/' . \S::seo( $row['name'] ) . '$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . \front\factory\Layouts::category_default_layout() . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
$htaccess_data .= 'RewriteRule ^producent/' . \S::seo( $row['name'] ) . '/([0-9]+)$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . \front\factory\Layouts::category_default_layout() . '&bs=$1&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
$htaccess_data .= 'RewriteRule ^producent/' . self::seo( $row['name'] ) . '$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId . '&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
$htaccess_data .= 'RewriteRule ^producent/' . self::seo( $row['name'] ) . '/([0-9]+)$ index.php?module=shop_producer&action=products&producer_id=' . $row['id'] . '&layout_id=' . $categoryDefaultLayoutId . '&bs=$1&%{QUERY_STRING} [L]' . PHP_EOL;
|
||||
}
|
||||
|
||||
$results = $mdb -> select( 'pp_langs', [ 'id', 'start' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] );
|
||||
@@ -597,9 +467,9 @@ class S
|
||||
{
|
||||
$site_map .= '<url>' . PHP_EOL;
|
||||
if ( $row2['seo_link'] )
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . \S::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . self::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
else
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . 'k-' . $row2['category_id'] . '-' . \S::seo( $row2['title'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>' . PHP_EOL;
|
||||
$site_map .= '<changefreq>daily</changefreq>' . PHP_EOL;
|
||||
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
||||
@@ -607,15 +477,15 @@ class S
|
||||
|
||||
if ( $row2['seo_link'] )
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . \S::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . self::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
||||
}
|
||||
else
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . \S::seo( $row2['title'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . \S::seo( $row2['title'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . \S::seo( $row2['title'] ) . '/1$ ' . $language_link . 'k-' . $row2['category_id'] . '-' . \S::seo( $row2['title'] ) . ' [R=301,L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '/([0-9]+)$ index.php?category=' . $row2['category_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . '/1$ ' . $language_link . 'k-' . $row2['category_id'] . '-' . self::seo( $row2['title'] ) . ' [R=301,L]';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,9 +509,9 @@ class S
|
||||
{
|
||||
$site_map .= '<url>' . PHP_EOL;
|
||||
if ( $row2['seo_link'] )
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . \S::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . self::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
else
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . 'p-' . $row2['product_id'] . '-' . \S::seo( $row2['name'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>' . PHP_EOL;
|
||||
$site_map .= '<changefreq>daily</changefreq>' . PHP_EOL;
|
||||
$site_map .= '<priority>1</priority>' . PHP_EOL;
|
||||
@@ -649,24 +519,24 @@ class S
|
||||
|
||||
if ( $row2['seo_link'] )
|
||||
{
|
||||
$pattern = '^' . $language_link . \S::seo( $row2['seo_link'] ) . '$';
|
||||
$pattern = '^' . $language_link . self::seo( $row2['seo_link'] ) . '$';
|
||||
$destination = 'index.php?product=' . $row2['product_id'];
|
||||
|
||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
||||
|
||||
$pattern = '^' . $language_link . \S::seo( $row2['seo_link'] ) . '/([0-9-]+)$';
|
||||
$pattern = '^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9-]+)$';
|
||||
$destination = 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1';
|
||||
|
||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . \S::seo( $row2['name'] ) . '$';
|
||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '$';
|
||||
$destination = 'index.php?product=' . $row2['product_id'];
|
||||
|
||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
||||
|
||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . \S::seo( $row2['name'] ) . '/([0-9-]+)$';
|
||||
$pattern = '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '/([0-9-]+)$';
|
||||
$destination = 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1';
|
||||
|
||||
$mdb -> insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => $pattern, 'destination' => $destination ] );
|
||||
@@ -693,7 +563,7 @@ class S
|
||||
{
|
||||
$site_map .= '<url>' . PHP_EOL;
|
||||
if ( $row2['seo_link'] )
|
||||
$site_map .= '<loc>https://' . $url . '/' . \S::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . self::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
else
|
||||
$site_map .= '<loc>https://' . $url . '/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>' . PHP_EOL;
|
||||
@@ -711,17 +581,17 @@ class S
|
||||
{
|
||||
if ( $row2['seo_link'] )
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . \S::seo( $row2['seo_link'] ) . '$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . self::seo( $row2['seo_link'] ) . '$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . \S::seo( $row2['seo_link'] ) . '-1$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/' . self::seo( $row2['seo_link'] ) . '-1$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||
}
|
||||
else
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . '$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . '-1$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteCond %{REQUEST_URI} ^/s-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '-1$';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^(.*)$ http://www.' . $url . '/' . $language_link . ' [R=permanent,L]';
|
||||
}
|
||||
|
||||
@@ -730,15 +600,15 @@ class S
|
||||
|
||||
if ( $row2['seo_link'] )
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . \S::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '/1$ ' . $language_link . self::seo( $row2['seo_link'] ) . ' [R=301,L]';
|
||||
}
|
||||
else
|
||||
{
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . '/1$ ' . $language_link . 's-' . $row2['page_id'] . '-' . \S::seo( $row2['title'] ) . ' [R=301,L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '/([0-9]+)$ index.php?a=page&id=' . $row2['page_id'] . '&lang=' . $row['id'] . '&bs=$1&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . '/1$ ' . $language_link . 's-' . $row2['page_id'] . '-' . self::seo( $row2['title'] ) . ' [R=301,L]';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -766,7 +636,7 @@ class S
|
||||
{
|
||||
$site_map .= '<url>' . PHP_EOL;
|
||||
if ( $row2['seo_link'] )
|
||||
$site_map .= '<loc>https://' . $url . '/' . \S::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<loc>https://' . $url . '/' . self::seo( $row2['seo_link'] ) . '</loc>' . PHP_EOL;
|
||||
else
|
||||
$site_map .= '<loc>https://' . $url . '/a-' . $row2['article_id'] . '-' . self::seo( $row2['title'] ) . '</loc>' . PHP_EOL;
|
||||
$site_map .= '<lastmod>' . date( 'Y-m-d' ) . '</lastmod>' . PHP_EOL;
|
||||
@@ -781,9 +651,9 @@ class S
|
||||
}
|
||||
|
||||
if ( $row2['seo_link'] )
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . \S::seo( $row2['seo_link'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . self::seo( $row2['seo_link'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
else if ( $row2['title'] != null )
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'a-' . $row2['article_id'] . '-' . \S::seo( $row2['title'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
$htaccess_data .= PHP_EOL . 'RewriteRule ^' . $language_link . 'a-' . $row2['article_id'] . '-' . self::seo( $row2['title'] ) . '$ index.php?article=' . $row2['article_id'] . '&lang=' . $row['id'] . '&%{QUERY_STRING} [L]';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,6 +723,10 @@ class S
|
||||
$htaccess_data .= 'RewriteCond %{REQUEST_FILENAME} !-d' . PHP_EOL;
|
||||
$htaccess_data .= 'RewriteRule ^ index.php [L]';
|
||||
|
||||
// Niektore hostingi blokuja zmiane wersji PHP przez .htaccess.
|
||||
// Automatycznie komentujemy niedozwolone dyrektywy, aby generowany plik byl kompatybilny.
|
||||
$htaccess_data = preg_replace( '/^(\\s*)(AddHandler|SetHandler|ForceType)\\b/im', '$1# $2', $htaccess_data );
|
||||
|
||||
$fp = fopen( $dir . '.htaccess', 'w' );
|
||||
fwrite( $fp, $htaccess_data );
|
||||
fclose( $fp );
|
||||
@@ -1019,16 +893,11 @@ class S
|
||||
foreach ( $scan as $index => $path )
|
||||
self::delete_dir( $path );
|
||||
|
||||
if ( is_dir( $dir ) && self::is_empty_dir( $dir ) )
|
||||
if ( is_dir( $dir ) && ( ( $files = @scandir( $dir ) ) && count( $files ) <= 2 ) )
|
||||
return @rmdir( $dir );
|
||||
}
|
||||
}
|
||||
|
||||
public static function is_empty_dir( $dir )
|
||||
{
|
||||
return ( ( $files = @scandir( $dir ) ) && count( $files ) <= 2 );
|
||||
}
|
||||
|
||||
public static function email_check( $email )
|
||||
{
|
||||
return filter_var( $email, FILTER_VALIDATE_EMAIL );
|
||||
@@ -1040,7 +909,7 @@ class S
|
||||
|
||||
if ( self::email_check( $email ) and $subject )
|
||||
{
|
||||
$mail = new PHPMailer();
|
||||
$mail = new \PHPMailer();
|
||||
$mail -> IsSMTP();
|
||||
$mail -> SMTPAuth = true;
|
||||
$mail -> Host = $settings['email_host'];
|
||||
@@ -1087,4 +956,20 @@ class S
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function shortPrice( $price )
|
||||
{
|
||||
if ( self::isWholeNumber( $price ) )
|
||||
$price = round( $price, 0 );
|
||||
else
|
||||
$price = self::decimal( $price );
|
||||
|
||||
return $price;
|
||||
}
|
||||
|
||||
public static function isWholeNumber( $value )
|
||||
{
|
||||
return ( is_numeric( $value ) && ( round( $value, 3 ) == round( $value ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,68 @@
|
||||
<?php
|
||||
namespace Shared\Html;
|
||||
|
||||
class Html
|
||||
{
|
||||
public static function form_text( array $params = array() )
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/form-text' );
|
||||
}
|
||||
|
||||
|
||||
public static function input_switch( array $params = array() )
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/input-switch' );
|
||||
}
|
||||
|
||||
|
||||
public static function select( array $params = array() )
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/select' );
|
||||
}
|
||||
|
||||
|
||||
public static function textarea( array $params = array() )
|
||||
{
|
||||
$defaults = array(
|
||||
'rows' => 4,
|
||||
);
|
||||
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
|
||||
$tpl = new \Tpl;
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/textarea' );
|
||||
}
|
||||
|
||||
|
||||
public static function input_icon( array $params = array() )
|
||||
{
|
||||
$defaults = array(
|
||||
'type' => 'text',
|
||||
);
|
||||
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
|
||||
$tpl = new \Tpl;
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/input-icon' );
|
||||
}
|
||||
|
||||
|
||||
public static function input( array $params = array() )
|
||||
{
|
||||
$defaults = array(
|
||||
'type' => 'text',
|
||||
);
|
||||
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
|
||||
$tpl = new \Tpl;
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/input' );
|
||||
}
|
||||
|
||||
|
||||
public static function button( array $params = array() )
|
||||
{
|
||||
$defaults = array(
|
||||
@@ -68,12 +70,12 @@ class Html
|
||||
);
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
|
||||
$tpl = new \Tpl;
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/button' );
|
||||
}
|
||||
|
||||
|
||||
public static function panel( array $params = array() )
|
||||
{
|
||||
$defaults = array(
|
||||
@@ -83,8 +85,8 @@ class Html
|
||||
);
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
|
||||
$tpl = new \Tpl;
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl -> params = $params;
|
||||
return $tpl -> render( 'html/panel' );
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
<?php
|
||||
namespace Shared\Image;
|
||||
|
||||
class ImageManipulator
|
||||
{
|
||||
protected int $width;
|
||||
protected int $height;
|
||||
protected \GdImage $image;
|
||||
/** @var resource|\GdImage */
|
||||
protected $image;
|
||||
protected ?string $file = null;
|
||||
|
||||
/**
|
||||
@@ -29,22 +32,22 @@ class ImageManipulator
|
||||
*
|
||||
* @param string $file Path to image file
|
||||
* @return self
|
||||
* @throws InvalidArgumentException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setImageFile(string $file): self
|
||||
{
|
||||
if (!(is_readable($file) && is_file($file))) {
|
||||
throw new InvalidArgumentException("Image file $file is not readable");
|
||||
throw new \InvalidArgumentException("Image file $file is not readable");
|
||||
}
|
||||
|
||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
||||
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
|
||||
[$width, $height, $type] = getimagesize($file);
|
||||
|
||||
if ($width === false || $height === false) {
|
||||
throw new InvalidArgumentException("Unable to get image size for $file");
|
||||
throw new \InvalidArgumentException("Unable to get image size for $file");
|
||||
}
|
||||
|
||||
error_log("Loaded image size from file: width: $width, height: $height, type: $type");
|
||||
@@ -63,11 +66,11 @@ class ImageManipulator
|
||||
$this->image = imagecreatefromwebp($file);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException("Image type $type not supported");
|
||||
throw new \InvalidArgumentException("Image type $type not supported");
|
||||
}
|
||||
|
||||
if (!$this->image instanceof \GdImage) {
|
||||
throw new InvalidArgumentException("Failed to create image from $file");
|
||||
if (!$this->isValidImageResource($this->image)) {
|
||||
throw new \InvalidArgumentException("Failed to create image from $file");
|
||||
}
|
||||
|
||||
$this->width = imagesx($this->image);
|
||||
@@ -76,7 +79,7 @@ class ImageManipulator
|
||||
error_log("Set image dimensions: width: {$this->width}, height: {$this->height}");
|
||||
|
||||
if ($this->width === 0 || $this->height === 0) {
|
||||
throw new InvalidArgumentException("Image dimensions are invalid (width: $this->width, height: $this->height)");
|
||||
throw new \InvalidArgumentException("Image dimensions are invalid (width: $this->width, height: $this->height)");
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -87,17 +90,17 @@ class ImageManipulator
|
||||
*
|
||||
* @param string $data Image data as string
|
||||
* @return self
|
||||
* @throws RuntimeException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function setImageString(string $data): self
|
||||
{
|
||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
||||
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
|
||||
$image = imagecreatefromstring($data);
|
||||
if (!$image instanceof \GdImage) {
|
||||
throw new RuntimeException('Cannot create image from data string');
|
||||
if (!$this->isValidImageResource($image)) {
|
||||
throw new \RuntimeException('Cannot create image from data string');
|
||||
}
|
||||
|
||||
$this->image = $image;
|
||||
@@ -107,7 +110,7 @@ class ImageManipulator
|
||||
error_log("Set image dimensions from string: width: {$this->width}, height: {$this->height}");
|
||||
|
||||
if ($this->width === 0 || $this->height === 0) {
|
||||
throw new RuntimeException("Image dimensions are invalid (width: $this->width, height: $this->height)");
|
||||
throw new \RuntimeException("Image dimensions are invalid (width: $this->width, height: $this->height)");
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -120,17 +123,17 @@ class ImageManipulator
|
||||
* @param int $height New height
|
||||
* @param bool $constrainProportions Constrain current image proportions when resizing
|
||||
* @return self
|
||||
* @throws RuntimeException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function resample(int $width, int $height, bool $constrainProportions = true): self
|
||||
{
|
||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
||||
throw new RuntimeException('No image set');
|
||||
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||
throw new \RuntimeException('No image set');
|
||||
}
|
||||
|
||||
if ($constrainProportions) {
|
||||
if ($this->height === 0) {
|
||||
throw new RuntimeException('Image height is zero, cannot calculate aspect ratio');
|
||||
throw new \RuntimeException('Image height is zero, cannot calculate aspect ratio');
|
||||
}
|
||||
|
||||
$aspectRatio = $this->width / $this->height;
|
||||
@@ -147,7 +150,7 @@ class ImageManipulator
|
||||
}
|
||||
|
||||
if ($width <= 0 || $height <= 0) {
|
||||
throw new RuntimeException('Calculated dimensions are invalid (width: ' . $width . ', height: ' . $height . ')');
|
||||
throw new \RuntimeException('Calculated dimensions are invalid (width: ' . $width . ', height: ' . $height . ')');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,20 +168,20 @@ class ImageManipulator
|
||||
* @param int|null $xpos X-Position of image in new canvas, null for centre
|
||||
* @param int|null $ypos Y-Position of image in new canvas, null for centre
|
||||
* @return self
|
||||
* @throws RuntimeException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function enlargeCanvas(int $width, int $height, array $rgb = [], ?int $xpos = null, ?int $ypos = null): self
|
||||
{
|
||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
||||
throw new RuntimeException('No image set');
|
||||
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||
throw new \RuntimeException('No image set');
|
||||
}
|
||||
|
||||
$width = max($width, $this->width);
|
||||
$height = max($height, $this->height);
|
||||
|
||||
$temp = imagecreatetruecolor($width, $height);
|
||||
if (!$temp instanceof \GdImage) {
|
||||
throw new RuntimeException('Failed to create a new image for enlarging canvas');
|
||||
if (!$this->isValidImageResource($temp)) {
|
||||
throw new \RuntimeException('Failed to create a new image for enlarging canvas');
|
||||
}
|
||||
|
||||
// Fill background if RGB provided
|
||||
@@ -215,7 +218,7 @@ class ImageManipulator
|
||||
$this->width,
|
||||
$this->height
|
||||
)) {
|
||||
throw new RuntimeException('Failed to copy image onto enlarged canvas');
|
||||
throw new \RuntimeException('Failed to copy image onto enlarged canvas');
|
||||
}
|
||||
|
||||
return $this->_replace($temp);
|
||||
@@ -229,12 +232,12 @@ class ImageManipulator
|
||||
* @param int $x2 Bottom right x-coordinate of crop box
|
||||
* @param int $y2 Bottom right y-coordinate of crop box
|
||||
* @return self
|
||||
* @throws RuntimeException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function crop($x1, int $y1 = 0, int $x2 = 0, int $y2 = 0): self
|
||||
{
|
||||
if (!isset($this->image) || !$this->image instanceof \GdImage) {
|
||||
throw new RuntimeException('No image set');
|
||||
if (!isset($this->image) || !$this->isValidImageResource($this->image)) {
|
||||
throw new \RuntimeException('No image set');
|
||||
}
|
||||
|
||||
if (is_array($x1) && count($x1) === 4) {
|
||||
@@ -253,12 +256,12 @@ class ImageManipulator
|
||||
error_log("Cropping image: x1: $x1, y1: $y1, x2: $x2, y2: $y2, cropWidth: $cropWidth, cropHeight: $cropHeight");
|
||||
|
||||
if ($cropWidth <= 0 || $cropHeight <= 0) {
|
||||
throw new RuntimeException('Invalid crop dimensions');
|
||||
throw new \RuntimeException('Invalid crop dimensions');
|
||||
}
|
||||
|
||||
$temp = imagecreatetruecolor($cropWidth, $cropHeight);
|
||||
if (!$temp instanceof \GdImage) {
|
||||
throw new RuntimeException('Failed to create a new image for cropping');
|
||||
if (!$this->isValidImageResource($temp)) {
|
||||
throw new \RuntimeException('Failed to create a new image for cropping');
|
||||
}
|
||||
|
||||
// Preserve transparency
|
||||
@@ -277,7 +280,7 @@ class ImageManipulator
|
||||
$cropWidth,
|
||||
$cropHeight
|
||||
)) {
|
||||
throw new RuntimeException('Failed to crop image');
|
||||
throw new \RuntimeException('Failed to crop image');
|
||||
}
|
||||
|
||||
return $this->_replace($temp);
|
||||
@@ -286,17 +289,17 @@ class ImageManipulator
|
||||
/**
|
||||
* Replace current image resource with a new one
|
||||
*
|
||||
* @param \GdImage $res New image resource
|
||||
* @param resource|\GdImage $res New image resource
|
||||
* @return self
|
||||
* @throws UnexpectedValueException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function _replace(\GdImage $res): self
|
||||
protected function _replace($res): self
|
||||
{
|
||||
if (!$res instanceof \GdImage) {
|
||||
throw new UnexpectedValueException('Invalid image resource');
|
||||
if (!$this->isValidImageResource($res)) {
|
||||
throw new \UnexpectedValueException('Invalid image resource');
|
||||
}
|
||||
|
||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
||||
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
|
||||
@@ -307,7 +310,7 @@ class ImageManipulator
|
||||
error_log("Replaced image dimensions: width: {$this->width}, height: {$this->height}");
|
||||
|
||||
if ($this->width === 0 || $this->height === 0) {
|
||||
throw new UnexpectedValueException("Replaced image has invalid dimensions (width: $this->width, height: $this->height)");
|
||||
throw new \UnexpectedValueException("Replaced image has invalid dimensions (width: $this->width, height: $this->height)");
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -319,14 +322,14 @@ class ImageManipulator
|
||||
* @param string $fileName Path to save the image
|
||||
* @param int|null $type Image type (IMAGETYPE_*) or null to auto-detect from file extension
|
||||
* @return void
|
||||
* @throws RuntimeException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function save(string $fileName, ?int $type = null): void
|
||||
{
|
||||
$dir = dirname($fileName);
|
||||
if (!is_dir($dir)) {
|
||||
if (!mkdir($dir, 0755, true) && !is_dir($dir)) {
|
||||
throw new RuntimeException('Error creating directory ' . $dir);
|
||||
throw new \RuntimeException('Error creating directory ' . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,37 +361,37 @@ class ImageManipulator
|
||||
switch ($type) {
|
||||
case IMAGETYPE_WEBP:
|
||||
if (!imagewebp($this->image, $fileName)) {
|
||||
throw new RuntimeException('Failed to save image as WEBP');
|
||||
throw new \RuntimeException('Failed to save image as WEBP');
|
||||
}
|
||||
break;
|
||||
case IMAGETYPE_GIF:
|
||||
if (!imagegif($this->image, $fileName)) {
|
||||
throw new RuntimeException('Failed to save image as GIF');
|
||||
throw new \RuntimeException('Failed to save image as GIF');
|
||||
}
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
if (!imagepng($this->image, $fileName)) {
|
||||
throw new RuntimeException('Failed to save image as PNG');
|
||||
throw new \RuntimeException('Failed to save image as PNG');
|
||||
}
|
||||
break;
|
||||
case IMAGETYPE_JPEG:
|
||||
default:
|
||||
if (!imagejpeg($this->image, $fileName, 95)) {
|
||||
throw new RuntimeException('Failed to save image as JPEG');
|
||||
throw new \RuntimeException('Failed to save image as JPEG');
|
||||
}
|
||||
}
|
||||
error_log("Image saved successfully to $fileName");
|
||||
} catch (\Exception $ex) {
|
||||
throw new RuntimeException('Error saving image file to ' . $fileName . ': ' . $ex->getMessage());
|
||||
throw new \RuntimeException('Error saving image file to ' . $fileName . ': ' . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GD image resource
|
||||
*
|
||||
* @return \GdImage
|
||||
* @return resource|\GdImage
|
||||
*/
|
||||
public function getResource(): \GdImage
|
||||
public function getResource()
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
@@ -418,8 +421,22 @@ class ImageManipulator
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (isset($this->image) && $this->image instanceof \GdImage) {
|
||||
if (isset($this->image) && $this->isValidImageResource($this->image)) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility helper for PHP 7.4 (resource) and PHP 8+ (GdImage).
|
||||
*
|
||||
* @param mixed $image
|
||||
*/
|
||||
private function isValidImageResource($image): bool
|
||||
{
|
||||
if (is_resource($image)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return class_exists('GdImage', false) && $image instanceof \GdImage;
|
||||
}
|
||||
}
|
||||
63
autoload/Shared/Tpl/Tpl.php
Normal file
63
autoload/Shared/Tpl/Tpl.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace Shared\Tpl;
|
||||
|
||||
class Tpl
|
||||
{
|
||||
protected $vars = array();
|
||||
|
||||
public static function view($file, $values = '')
|
||||
{
|
||||
$tpl = new self;
|
||||
if (is_array($values)) foreach ($values as $key => $val)
|
||||
$tpl->$key = $val;
|
||||
return $tpl->render($file);
|
||||
}
|
||||
|
||||
public function secureHTML($val)
|
||||
{
|
||||
$out = stripslashes($val);
|
||||
$out = str_replace("'", "'", $out);
|
||||
$out = str_replace('"', """, $out);
|
||||
$out = str_replace("<", "<", $out);
|
||||
$out = str_replace(">", ">", $out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function render($file)
|
||||
{
|
||||
$paths = [
|
||||
'templates_user/' . $file . '.php',
|
||||
'templates/' . $file . '.php',
|
||||
'../templates_user/' . $file . '.php',
|
||||
'../templates/' . $file . '.php',
|
||||
$file . '.php',
|
||||
];
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
return $this->renderFile($path);
|
||||
}
|
||||
}
|
||||
|
||||
return '<div class="alert alert-danger" role="alert">Nie znaleziono pliku widoku: <b>' . $file . '.php</b>';
|
||||
}
|
||||
|
||||
private function renderFile($path)
|
||||
{
|
||||
ob_start();
|
||||
include $path;
|
||||
$out = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->vars[$name] = $value;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->vars[$name];
|
||||
}
|
||||
}
|
||||
449
autoload/admin/App.php
Normal file
449
autoload/admin/App.php
Normal file
@@ -0,0 +1,449 @@
|
||||
<?php
|
||||
namespace admin;
|
||||
|
||||
class App
|
||||
{
|
||||
const APP_SECRET_KEY = 'c3cb2537d25c0efc9e573d059d79c3b8';
|
||||
|
||||
/**
|
||||
* Mapa nowych kontrolerów: module => fabryka kontrolera (DI)
|
||||
*/
|
||||
private static $newControllers = [];
|
||||
|
||||
public static function finalize_admin_login( array $user, string $domain, string $cookie_name, bool $remember = false )
|
||||
{
|
||||
\Shared\Helpers\Helpers::set_session( 'user', $user );
|
||||
\Shared\Helpers\Helpers::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 = \Shared\Helpers\Helpers::get( 's-action' );
|
||||
if ( !$sa ) return;
|
||||
|
||||
$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 = \Shared\Helpers\Helpers::get( 'login' );
|
||||
$pass = \Shared\Helpers\Helpers::get( 'password' );
|
||||
$result = $users->logon( $login, $pass );
|
||||
|
||||
if ( $result == 1 )
|
||||
{
|
||||
$user = $users->details( $login );
|
||||
if ( !$user ) {
|
||||
\Shared\Helpers\Helpers::alert( 'Błąd logowania.' );
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $user['twofa_enabled'] == 1 )
|
||||
{
|
||||
\Shared\Helpers\Helpers::set_session( 'twofa_pending', [
|
||||
'uid' => (int) $user['id'],
|
||||
'login' => $login,
|
||||
'remember' => (bool) \Shared\Helpers\Helpers::get( 'remember' ),
|
||||
'started' => time(),
|
||||
] );
|
||||
|
||||
if ( !$users->sendTwofaCode( (int) $user['id'] ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( 'Nie udało się wysłać kodu 2FA. Spróbuj ponownie.' );
|
||||
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
header( 'Location: /admin/user/twofa/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
self::finalize_admin_login( $user, $domain, $cookie_name, (bool) \Shared\Helpers\Helpers::get( 'remember' ) );
|
||||
header( 'Location: /admin/articles/list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $result == -1 )
|
||||
\Shared\Helpers\Helpers::alert( 'Z powodu 5 nieudanych prób Twoje konto zostało zablokowane.' );
|
||||
else
|
||||
\Shared\Helpers\Helpers::alert( 'Podane hasło jest nieprawidłowe lub użytkownik nie istnieje.' );
|
||||
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
|
||||
case 'user-2fa-verify':
|
||||
$pending = \Shared\Helpers\Helpers::get_session( 'twofa_pending' );
|
||||
if ( !$pending || empty( $pending['uid'] ) ) {
|
||||
\Shared\Helpers\Helpers::alert( 'Sesja 2FA wygasła. Zaloguj się ponownie.' );
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$code = trim( (string) \Shared\Helpers\Helpers::get( 'twofa' ) );
|
||||
if ( !preg_match( '/^\d{6}$/', $code ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( 'Nieprawidłowy format kodu.' );
|
||||
header( 'Location: /admin/user/twofa/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( !$users->verifyTwofaCode( (int) $pending['uid'], $code ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( 'Błędny lub wygasły kod.' );
|
||||
header( 'Location: /admin/user/twofa/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = $users->details( $pending['login'] );
|
||||
if ( !$user ) {
|
||||
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
|
||||
\Shared\Helpers\Helpers::alert( 'Sesja wygasła. Zaloguj się ponownie.' );
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
self::finalize_admin_login( $user, $domain, $cookie_name, !empty( $pending['remember'] ) );
|
||||
header( 'Location: /admin/articles/list/' );
|
||||
exit;
|
||||
|
||||
case 'user-2fa-resend':
|
||||
$pending = \Shared\Helpers\Helpers::get_session( 'twofa_pending' );
|
||||
if ( !$pending || empty( $pending['uid'] ) )
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert( 'Sesja 2FA wygasła. Zaloguj się ponownie.' );
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( !$users->sendTwofaCode( (int) $pending['uid'], true ) )
|
||||
\Shared\Helpers\Helpers::alert( 'Kod można wysłać ponownie po krótkiej przerwie.' );
|
||||
else
|
||||
\Shared\Helpers\Helpers::alert( 'Nowy kod został wysłany.' );
|
||||
|
||||
header( 'Location: /admin/user/twofa/' );
|
||||
exit;
|
||||
|
||||
case 'user-logout':
|
||||
setcookie( $cookie_name, '', [
|
||||
'expires' => time() - 86400,
|
||||
'path' => '/',
|
||||
'domain' => $domain,
|
||||
'secure' => true,
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
] );
|
||||
\Shared\Helpers\Helpers::delete_session( 'twofa_pending' );
|
||||
session_destroy();
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point — auth check + layout rendering.
|
||||
*/
|
||||
public static function render(): string
|
||||
{
|
||||
global $user;
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get( 'module' ) === 'user' && \Shared\Helpers\Helpers::get( 'action' ) === 'twofa' ) {
|
||||
$controller = self::createController( 'Users' );
|
||||
return $controller->twofa();
|
||||
}
|
||||
|
||||
if ( !$user || !$user['admin'] )
|
||||
{
|
||||
$controller = self::createController( 'Users' );
|
||||
return $controller->login_form();
|
||||
}
|
||||
|
||||
$tpl = new \Shared\Tpl\Tpl;
|
||||
$tpl->content = self::route();
|
||||
return $tpl->render( 'site/main-layout' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing — buduje nazwę modułu z URL i wywołuje akcję kontrolera.
|
||||
*/
|
||||
public static function route()
|
||||
{
|
||||
$_SESSION['admin'] = true;
|
||||
|
||||
if ( \Shared\Helpers\Helpers::get( 'p' ) )
|
||||
\Shared\Helpers\Helpers::set_session( 'p', \Shared\Helpers\Helpers::get( 'p' ) );
|
||||
|
||||
// Budowanie nazwy modułu: shop_product → ShopProduct
|
||||
$moduleName = '';
|
||||
$parts = explode( '_', (string) \Shared\Helpers\Helpers::get( 'module' ) );
|
||||
foreach ( $parts as $part )
|
||||
$moduleName .= ucfirst( $part );
|
||||
|
||||
$action = \Shared\Helpers\Helpers::get( 'action' );
|
||||
|
||||
$controller = self::createController( $moduleName );
|
||||
if ( $controller && method_exists( $controller, $action ) )
|
||||
return $controller->$action();
|
||||
|
||||
\Shared\Helpers\Helpers::alert( 'Nieprawidłowy adres url.' );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tworzy instancję kontrolera z Dependency Injection.
|
||||
*/
|
||||
private static function createController( string $moduleName )
|
||||
{
|
||||
$factories = self::getControllerFactories();
|
||||
if ( !isset( $factories[$moduleName] ) )
|
||||
return null;
|
||||
|
||||
$factory = $factories[$moduleName];
|
||||
return is_callable( $factory ) ? $factory() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca mapę fabryk kontrolerów (lazy init).
|
||||
*/
|
||||
private static function getControllerFactories(): array
|
||||
{
|
||||
if ( !empty( self::$newControllers ) )
|
||||
return self::$newControllers;
|
||||
|
||||
self::$newControllers = [
|
||||
'Dashboard' => function() {
|
||||
global $mdb;
|
||||
return new \admin\Controllers\DashboardController(
|
||||
new \Domain\Dashboard\DashboardRepository( $mdb ),
|
||||
new \Domain\ShopStatus\ShopStatusRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'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 )
|
||||
);
|
||||
},
|
||||
'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 ),
|
||||
new \Domain\Integrations\IntegrationsRepository( $mdb ),
|
||||
new \Domain\Languages\LanguagesRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopClients' => function() {
|
||||
global $mdb;
|
||||
return new \admin\Controllers\ShopClientsController(
|
||||
new \Domain\Client\ClientRepository( $mdb )
|
||||
);
|
||||
},
|
||||
'ShopOrder' => function() {
|
||||
global $mdb;
|
||||
$productRepo = new \Domain\Product\ProductRepository( $mdb );
|
||||
return new \admin\Controllers\ShopOrderController(
|
||||
new \Domain\Order\OrderAdminService(
|
||||
new \Domain\Order\OrderRepository( $mdb ),
|
||||
$productRepo,
|
||||
new \Domain\Settings\SettingsRepository( $mdb ),
|
||||
new \Domain\Transport\TransportRepository( $mdb )
|
||||
),
|
||||
$productRepo
|
||||
);
|
||||
},
|
||||
'Update' => function() {
|
||||
global $mdb;
|
||||
return new \admin\Controllers\UpdateController(
|
||||
new \Domain\Update\UpdateRepository( $mdb )
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
return self::$newControllers;
|
||||
}
|
||||
|
||||
public static function update()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$repository = new \Domain\Update\UpdateRepository( $mdb );
|
||||
$repository->runPendingMigrations();
|
||||
}
|
||||
}
|
||||
147
autoload/admin/Controllers/ArticlesArchiveController.php
Normal file
147
autoload/admin/Controllers/ArticlesArchiveController.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Article\ArticleRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
|
||||
class ArticlesArchiveController
|
||||
{
|
||||
private ArticleRepository $repository;
|
||||
|
||||
public function __construct(ArticleRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['title', 'date_add', 'date_modify'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'title',
|
||||
'label' => 'Tytul',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'date_add'
|
||||
);
|
||||
|
||||
$result = $this->repository->listArchivedForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$listRequest['sortDir'],
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$title = trim((string)($item['title'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'title' => '<a href="/admin/articles/edit/id=' . $id . '">' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'date_add' => !empty($item['date_add']) ? date('Y-m-d H:i', strtotime((string)$item['date_add'])) : '-',
|
||||
'date_modify' => !empty($item['date_modify']) ? date('Y-m-d H:i', strtotime((string)$item['date_modify'])) : '-',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Przywroc',
|
||||
'url' => '/admin/articles_archive/restore/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-success',
|
||||
'confirm' => 'Na pewno chcesz przywrocic wybrany artykul?',
|
||||
'confirm_ok' => 'Przywroc',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/articles_archive/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz trwale usunac wybrany artykul?',
|
||||
'confirm_ok' => 'Usun',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'title', 'sort_key' => 'title', 'label' => 'Tytul', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'date_add', 'sort_key' => 'date_add', 'label' => 'Data dodania', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'date_modify', 'sort_key' => 'date_modify', 'label' => 'Data modyfikacji', 'class' => 'text-center', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/articles_archive/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('articles/articles-archive-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function restore(): void
|
||||
{
|
||||
if ($this->repository->restore((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Artykul zostal przywrocony.');
|
||||
}
|
||||
|
||||
header('Location: /admin/articles_archive/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function article_restore(): void
|
||||
{
|
||||
$this->restore();
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->deletePermanently((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Artykul zostal trwale usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/articles_archive/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function article_delete(): void
|
||||
{
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
509
autoload/admin/Controllers/ArticlesController.php
Normal file
509
autoload/admin/Controllers/ArticlesController.php
Normal file
@@ -0,0 +1,509 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Article\ArticleRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use Domain\Layouts\LayoutsRepository;
|
||||
use Domain\Pages\PagesRepository;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ArticlesController
|
||||
{
|
||||
private ArticleRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private LayoutsRepository $layoutsRepository;
|
||||
private PagesRepository $pagesRepository;
|
||||
|
||||
public function __construct(
|
||||
ArticleRepository $repository,
|
||||
LanguagesRepository $languagesRepository,
|
||||
LayoutsRepository $layoutsRepository,
|
||||
PagesRepository $pagesRepository
|
||||
)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->layoutsRepository = $layoutsRepository;
|
||||
$this->pagesRepository = $pagesRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista artykulow
|
||||
*/
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['title', 'status', 'date_add', 'date_modify'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'title',
|
||||
'label' => 'Tytul',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'date_add'
|
||||
);
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$listRequest['sortDir'],
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$articleIds = [];
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
$articleIds[] = $id;
|
||||
}
|
||||
}
|
||||
$pagesSummary = $this->repository->pagesSummaryForArticles($articleIds);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$title = (string)($item['title'] ?? '');
|
||||
$pages = (string)($pagesSummary[$id] ?? '');
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'title' => '<a href="/admin/articles/edit/id=' . $id . '">' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '</a>'
|
||||
. '<small class="text-muted">' . htmlspecialchars($pages, ENT_QUOTES, 'UTF-8') . '</small>',
|
||||
'status' => ((int)$item['status'] === 1) ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'date_add' => !empty($item['date_add']) ? date('Y-m-d H:i', strtotime((string)$item['date_add'])) : '-',
|
||||
'date_modify' => !empty($item['date_modify']) ? date('Y-m-d H:i', strtotime((string)$item['date_modify'])) : '-',
|
||||
'user' => htmlspecialchars((string)($item['user'] ?? ''), ENT_QUOTES, 'UTF-8'),
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/articles/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/articles/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany element?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'title', 'sort_key' => 'title', 'label' => 'Tytul', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'date_add', 'sort_key' => 'date_add', 'label' => 'Data dodania', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'date_modify', 'sort_key' => 'date_modify', 'label' => 'Data modyfikacji', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'user', 'sort_key' => 'user', 'label' => 'Modyfikowany przez', 'class' => 'text-center', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/articles/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/articles/edit/',
|
||||
'Dodaj artykul'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('articles/articles-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis kolejnosci galerii (AJAX)
|
||||
*/
|
||||
public function galleryOrderSave(): void
|
||||
{
|
||||
if ($this->repository->saveGalleryOrder((int)\Shared\Helpers\Helpers::get('article_id'), (string)\Shared\Helpers\Helpers::get('order'))) {
|
||||
echo json_encode(['status' => 'ok', 'msg' => 'Artykul zostal zapisany.']);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis kolejnosci zalacznikow (AJAX)
|
||||
*/
|
||||
public function filesOrderSave(): void
|
||||
{
|
||||
if ($this->repository->saveFilesOrder((int)\Shared\Helpers\Helpers::get('article_id'), (string)\Shared\Helpers\Helpers::get('order'))) {
|
||||
echo json_encode(['status' => 'ok', 'msg' => 'Artykul zostal zapisany.']);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis artykulu (AJAX)
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
global $user;
|
||||
|
||||
$values = $this->resolveSavePayload();
|
||||
$articleId = (int)($values['id'] ?? \Shared\Helpers\Helpers::get('id') ?? 0);
|
||||
$id = $this->repository->save($articleId, $values, (int)$user['id']);
|
||||
|
||||
if ($id) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'status' => 'ok',
|
||||
'message' => 'Artykul zostal zapisany.',
|
||||
'msg' => 'Artykul zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'status' => 'error',
|
||||
'message' => 'Podczas zapisywania artykulu wystapil blad. Prosze sprobowac ponownie.',
|
||||
'msg' => 'Podczas zapisywania artykulu wystapil blad. Prosze sprobowac ponownie.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function imageAltChange(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zmiany atrybutu alt zdjecia wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->updateImageAlt((int)\Shared\Helpers\Helpers::get('image_id'), (string)\Shared\Helpers\Helpers::get('image_alt'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function fileNameChange(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zmiany nazwy zalacznika wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->updateFileName((int)\Shared\Helpers\Helpers::get('file_id'), (string)\Shared\Helpers\Helpers::get('file_name'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function imageDelete(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas usuwania zdjecia wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->markImageToDelete((int)\Shared\Helpers\Helpers::get('image_id'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function fileDelete(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas usuwania zalacznika wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->markFileToDelete((int)\Shared\Helpers\Helpers::get('file_id'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Archiwizacja artykulu (ustawia status = -1)
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->archive((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Artykul zostal przeniesiony do archiwum.');
|
||||
}
|
||||
|
||||
header('Location: /admin/articles/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edycja artykulu
|
||||
*/
|
||||
public function edit(): string
|
||||
{
|
||||
global $user;
|
||||
|
||||
if (!$user) {
|
||||
header('Location: /admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->repository->deleteNonassignedImages();
|
||||
$this->repository->deleteNonassignedFiles();
|
||||
|
||||
$article = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: ['id' => 0, 'languages' => [], 'images' => [], 'files' => [], 'pages' => []];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$menus = $this->pagesRepository->menusList();
|
||||
$layouts = $this->layoutsRepository->listAll();
|
||||
|
||||
$viewModel = $this->buildFormViewModel($article, $languages, $menus, $layouts);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('articles/article-edit', [
|
||||
'form' => $viewModel,
|
||||
'article' => $article,
|
||||
'user' => $user,
|
||||
]);
|
||||
}
|
||||
|
||||
private function resolveSavePayload(): array
|
||||
{
|
||||
$legacyValuesRaw = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValuesRaw !== null && $legacyValuesRaw !== '') {
|
||||
$legacyValues = json_decode((string)$legacyValuesRaw, true);
|
||||
if (is_array($legacyValues)) {
|
||||
return $legacyValues;
|
||||
}
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
unset($payload['_form_id']);
|
||||
|
||||
return is_array($payload) ? $payload : [];
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $article, array $languages, array $menus, array $layouts): FormEditViewModel
|
||||
{
|
||||
$articleId = (int)($article['id'] ?? 0);
|
||||
$defaultLanguageId = (string)($this->languagesRepository->defaultLanguageId() ?? 'pl');
|
||||
$title = $articleId > 0
|
||||
? 'Edycja artykulu: <u>' . $this->escapeHtml((string)($article['languages'][$defaultLanguageId]['title'] ?? '')) . '</u>'
|
||||
: 'Edycja artykulu';
|
||||
|
||||
$layoutOptions = ['' => '---- szablon domyslny ----'];
|
||||
foreach ($layouts as $layout) {
|
||||
$layoutOptions[(string)$layout['id']] = (string)$layout['name'];
|
||||
}
|
||||
|
||||
$copyFromOptions = ['' => '---- wersja jezykowa ----'];
|
||||
foreach ($languages as $language) {
|
||||
if (!empty($language['id'])) {
|
||||
$copyFromOptions[(string)$language['id']] = (string)$language['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$tabs = [
|
||||
new FormTab('content', 'Tresc', 'fa-file'),
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('seo', 'SEO', 'fa-globe'),
|
||||
new FormTab('display', 'Wyswietlanie', 'fa-share-alt'),
|
||||
new FormTab('gallery', 'Galeria', 'fa-file-image-o'),
|
||||
new FormTab('files', 'Zalaczniki', 'fa-file-archive-o'),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $articleId),
|
||||
FormField::langSection('article_content', 'content', [
|
||||
FormField::select('copy_from', [
|
||||
'label' => 'Wyswietlaj tresc z wersji',
|
||||
'options' => $copyFromOptions,
|
||||
]),
|
||||
FormField::text('title', [
|
||||
'label' => 'Tytul',
|
||||
'required' => true,
|
||||
'attributes' => ['id' => 'title'],
|
||||
]),
|
||||
FormField::image('main_image', [
|
||||
'label' => 'Zdjecie tytulowe',
|
||||
'filemanager' => true,
|
||||
'attributes' => ['id' => 'main_image'],
|
||||
]),
|
||||
FormField::editor('entry', [
|
||||
'label' => 'Wstep',
|
||||
'toolbar' => 'MyToolbar',
|
||||
'height' => 250,
|
||||
'attributes' => ['id' => 'entry'],
|
||||
]),
|
||||
FormField::editor('text', [
|
||||
'label' => 'Tresc',
|
||||
'toolbar' => 'MyToolbar',
|
||||
'height' => 250,
|
||||
'attributes' => ['id' => 'text'],
|
||||
]),
|
||||
FormField::editor('table_of_contents', [
|
||||
'label' => 'Spis tresci',
|
||||
'toolbar' => 'MyToolbar',
|
||||
'height' => 250,
|
||||
'attributes' => ['id' => 'table_of_contents'],
|
||||
]),
|
||||
]),
|
||||
FormField::switch('status', ['label' => 'Opublikowany', 'tab' => 'settings', 'value' => ((int)($article['status'] ?? 0) === 1) || $articleId === 0]),
|
||||
FormField::switch('show_title', ['label' => 'Pokaz tytul', 'tab' => 'settings', 'value' => (int)($article['show_title'] ?? 0) === 1]),
|
||||
FormField::switch('show_table_of_contents', ['label' => 'Pokaz spis tresci', 'tab' => 'settings', 'value' => (int)($article['show_table_of_contents'] ?? 0) === 1]),
|
||||
FormField::switch('show_date_add', ['label' => 'Pokaz date dodania', 'tab' => 'settings', 'value' => (int)($article['show_date_add'] ?? 0) === 1]),
|
||||
FormField::switch('show_date_modify', ['label' => 'Pokaz date modyfikacji', 'tab' => 'settings', 'value' => (int)($article['show_date_modify'] ?? 0) === 1]),
|
||||
FormField::switch('repeat_entry', ['label' => 'Powtorz wprowadzenie', 'tab' => 'settings', 'value' => (int)($article['repeat_entry'] ?? 0) === 1]),
|
||||
FormField::switch('social_icons', ['label' => 'Linki do portali spolecznosciowych', 'tab' => 'settings', 'value' => (int)($article['social_icons'] ?? 0) === 1]),
|
||||
FormField::langSection('article_seo', 'seo', [
|
||||
FormField::text('seo_link', ['label' => 'Link SEO', 'attributes' => ['id' => 'seo_link']]),
|
||||
FormField::text('meta_title', ['label' => 'Meta title']),
|
||||
FormField::textarea('meta_description', ['label' => 'Meta description', 'rows' => 4]),
|
||||
FormField::textarea('meta_keywords', ['label' => 'Meta keywords', 'rows' => 4]),
|
||||
FormField::switch('noindex', ['label' => 'Blokuj indeksacje']),
|
||||
FormField::switch('block_direct_access', ['label' => 'Blokuj bezposredni dostep']),
|
||||
]),
|
||||
FormField::select('layout_id', [
|
||||
'label' => 'Szablon',
|
||||
'tab' => 'display',
|
||||
'options' => $layoutOptions,
|
||||
'value' => $article['layout_id'] ?? '',
|
||||
]),
|
||||
FormField::custom('pages_tree', $this->renderPagesTree($menus, $article), ['tab' => 'display']),
|
||||
FormField::custom('images_box', $this->renderImagesBox($article), ['tab' => 'gallery']),
|
||||
FormField::custom('files_box', $this->renderFilesBox($article), ['tab' => 'files']),
|
||||
];
|
||||
|
||||
$actions = [
|
||||
FormAction::save('/admin/articles/save/' . ($articleId > 0 ? 'id=' . $articleId : ''), '/admin/articles/list/'),
|
||||
FormAction::cancel('/admin/articles/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'article-edit',
|
||||
$title,
|
||||
$article,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
'/admin/articles/save/' . ($articleId > 0 ? 'id=' . $articleId : ''),
|
||||
'/admin/articles/list/',
|
||||
true,
|
||||
['id' => $articleId],
|
||||
$languages
|
||||
);
|
||||
}
|
||||
|
||||
private function renderPagesTree(array $menus, array $article): string
|
||||
{
|
||||
$html = '<div class="form-group row">';
|
||||
$html .= '<label class="col-lg-4 control-label">Wyswietlaj na:</label>';
|
||||
$html .= '<div class="col-lg-8">';
|
||||
|
||||
foreach ($menus as $menu) {
|
||||
$menuId = (int)($menu['id'] ?? 0);
|
||||
$menuName = $this->escapeHtml((string)($menu['name'] ?? ''));
|
||||
$menuStatus = (int)($menu['status'] ?? 0);
|
||||
$menuPages = $this->pagesRepository->menuPages($menuId);
|
||||
|
||||
$html .= '<div class="menu_sortable">';
|
||||
$html .= '<ol class="sortable" id="sortable_' . $menuId . '">';
|
||||
$html .= '<li id="list_' . $menuId . '" class="menu_' . $menuId . '" menu="' . $menuId . '">';
|
||||
$html .= '<div class="context_0 content content_menu"' . ($menuStatus ? '' : ' style="color: #cc0000;"') . '>';
|
||||
$html .= '<button type="button" class="disclose layout-tree-toggle" aria-expanded="false" title="Rozwin / zwin">'
|
||||
. '<i class="fa fa-caret-right"></i>'
|
||||
. '</button>Menu: <b>' . $menuName . '</b>';
|
||||
$html .= '</div>';
|
||||
$html .= \Shared\Tpl\Tpl::view('articles/subpages-list', [
|
||||
'pages' => $menuPages,
|
||||
'article_pages' => $article['pages'] ?? [],
|
||||
'parent_id' => $menuId,
|
||||
'step' => 1,
|
||||
]);
|
||||
$html .= '</li></ol></div>';
|
||||
}
|
||||
|
||||
$html .= '</div><div class="clear"></div></div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function renderImagesBox(array $article): string
|
||||
{
|
||||
$html = '<ul id="images-list">';
|
||||
$images = is_array($article['images'] ?? null) ? $article['images'] : [];
|
||||
foreach ($images as $img) {
|
||||
$id = (int)($img['id'] ?? 0);
|
||||
$src = $this->escapeHtml((string)($img['src'] ?? ''));
|
||||
$alt = $this->escapeHtml((string)($img['alt'] ?? ''));
|
||||
|
||||
$html .= '<li id="image-' . $id . '">';
|
||||
$html .= '<img class="article-image lozad" data-src="/libraries/thumb.php?img=' . $src . '&w=300&h=300">';
|
||||
$html .= '<a href="#" class="input-group-addon btn btn-danger article_image_delete" image-id="' . $id . '"><i class="fa fa-trash"></i></a>';
|
||||
$html .= '<input type="text" class="form-control image-alt" value="' . $alt . '" image-id="' . $id . '" placeholder="atrybut alt...">';
|
||||
$html .= '</li>';
|
||||
}
|
||||
$html .= '</ul><div id="images-uploader">You browser doesn\'t have Flash installed.</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function renderFilesBox(array $article): string
|
||||
{
|
||||
$html = '<ul id="files-list">';
|
||||
$files = is_array($article['files'] ?? null) ? $article['files'] : [];
|
||||
foreach ($files as $file) {
|
||||
$id = (int)($file['id'] ?? 0);
|
||||
$src = (string)($file['src'] ?? '');
|
||||
$name = trim((string)($file['name'] ?? ''));
|
||||
if ($name === '') {
|
||||
$parts = explode('/', $src);
|
||||
$name = (string)end($parts);
|
||||
}
|
||||
$name = $this->escapeHtml($name);
|
||||
|
||||
$html .= '<li id="file-' . $id . '"><div class="input-group">';
|
||||
$html .= '<input type="text" class="article_file_edit form-control" file_id="' . $id . '" value="' . $name . '" />';
|
||||
$html .= '<a href="#" class="input-group-addon btn btn-info article_file_delete" file_id="' . $id . '"><i class="fa fa-trash"></i></a>';
|
||||
$html .= '</div></li>';
|
||||
}
|
||||
$html .= '</ul><div id="files-uploader">You browser doesn\'t have Flash installed.</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function escapeHtml(string $value): string
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
339
autoload/admin/Controllers/BannerController.php
Normal file
339
autoload/admin/Controllers/BannerController.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Banner\BannerRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class BannerController
|
||||
{
|
||||
private BannerRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(BannerRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista banerow
|
||||
*/
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['name', 'status', 'home_page', 'date_start', 'date_end'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
// Historycznie lista banerow domyslnie byla sortowana rosnaco po nazwie.
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$name = (string)($item['name'] ?? '');
|
||||
$homePage = (int)($item['home_page'] ?? 0);
|
||||
$isActive = (int)($item['status'] ?? 0) === 1;
|
||||
$thumbnailSrc = trim((string)($item['thumbnail_src'] ?? ''));
|
||||
if ($thumbnailSrc !== '' && !preg_match('#^(https?:)?//#i', $thumbnailSrc) && strpos($thumbnailSrc, '/') !== 0) {
|
||||
$thumbnailSrc = '/' . ltrim($thumbnailSrc, '/');
|
||||
}
|
||||
|
||||
$thumbnail = '<span class="text-muted">-</span>';
|
||||
if ($thumbnailSrc !== '') {
|
||||
$thumbnail = '<div class="banner-thumb-wrap">'
|
||||
. '<img src="' . htmlspecialchars($thumbnailSrc, ENT_QUOTES, 'UTF-8') . '" alt="" '
|
||||
. 'data-preview-src="' . htmlspecialchars($thumbnailSrc, ENT_QUOTES, 'UTF-8') . '" '
|
||||
. 'class="banner-thumb-image js-banner-thumb-preview" '
|
||||
. 'loading="lazy">'
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'thumbnail' => $thumbnail,
|
||||
'name' => '<a href="/admin/banners/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'status' => $isActive ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'home_page' => $homePage === 1 ? '<span class="text-system">tak</span>' : 'nie',
|
||||
'slider' => $homePage === 1 ? 'nie' : '<span class="text-system">tak</span>',
|
||||
'date_start' => !empty($item['date_start']) ? date('Y-m-d', strtotime((string)$item['date_start'])) : '-',
|
||||
'date_end' => !empty($item['date_end']) ? date('Y-m-d', strtotime((string)$item['date_end'])) : '-',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/banners/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/banners/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany element?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'thumbnail', 'label' => 'Miniatura', 'class' => 'text-center', 'sortable' => false, 'raw' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'home_page', 'sort_key' => 'home_page', 'label' => 'Strona glowna', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'slider', 'label' => 'Slajder', 'class' => 'text-center', 'sortable' => false, 'raw' => true],
|
||||
['key' => 'date_start', 'sort_key' => 'date_start', 'label' => 'Data rozpoczecia', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'date_end', 'sort_key' => 'date_end', 'label' => 'Data zakonczenia', 'class' => 'text-center', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/banners/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/banners/edit/',
|
||||
'Dodaj baner',
|
||||
'banners/banners-list-custom-script'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('banners/banners-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edycja banera
|
||||
*/
|
||||
public function edit(): string
|
||||
{
|
||||
$bannerId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$banner = $this->repository->find($bannerId) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
// Sprawdź czy są błędy walidacji z poprzedniego requestu
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getFormId()]);
|
||||
}
|
||||
|
||||
$viewModel = $this->buildFormViewModel($banner, $languages, $validationErrors);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('components/form-edit', ['form' => $viewModel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisanie banera (AJAX)
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
$response = ['success' => false, 'errors' => []];
|
||||
|
||||
$bannerId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$banner = $this->repository->find($bannerId) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
$viewModel = $this->buildFormViewModel($banner, $languages);
|
||||
|
||||
// Przetwórz dane z POST
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
|
||||
if (!$result['success']) {
|
||||
// Zapisz błędy w sesji i zwróć jako JSON
|
||||
$_SESSION['form_errors'][$this->getFormId()] = $result['errors'];
|
||||
$response['errors'] = $result['errors'];
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Zapisz dane
|
||||
$data = $result['data'];
|
||||
$data['id'] = $bannerId ?: null;
|
||||
|
||||
$savedId = $this->repository->save($data);
|
||||
|
||||
if ($savedId) {
|
||||
$response = [
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Baner został zapisany.'
|
||||
];
|
||||
} else {
|
||||
$response['errors'] = ['general' => 'Błąd podczas zapisywania do bazy.'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuniecie banera
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
$bannerId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($this->repository->delete($bannerId)) {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::alert('Baner zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/banners/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buduje model widoku formularza
|
||||
*/
|
||||
private function buildFormViewModel(array $banner, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$bannerId = $banner['id'] ?? 0;
|
||||
$isNew = empty($bannerId);
|
||||
|
||||
// Domyślne wartości dla nowego banera
|
||||
if ($isNew) {
|
||||
$banner['status'] = 1;
|
||||
$banner['home_page'] = 0;
|
||||
}
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('content', 'Zawartość', 'fa-file'),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
// Zakładka Ustawienia
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'settings',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
'value' => ($banner['status'] ?? 1) == 1,
|
||||
]),
|
||||
FormField::date('date_start', [
|
||||
'label' => 'Data rozpoczęcia',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::date('date_end', [
|
||||
'label' => 'Data zakończenia',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::switch('home_page', [
|
||||
'label' => 'Slajder / Strona główna',
|
||||
'tab' => 'settings',
|
||||
'value' => ($banner['home_page'] ?? 0) == 1,
|
||||
]),
|
||||
|
||||
// Sekcja językowa w zakładce Zawartość
|
||||
FormField::langSection('translations', 'content', [
|
||||
FormField::image('src', [
|
||||
'label' => 'Obraz',
|
||||
'filemanager' => true,
|
||||
]),
|
||||
FormField::text('url', [
|
||||
'label' => 'Url',
|
||||
]),
|
||||
FormField::textarea('html', [
|
||||
'label' => 'Kod HTML',
|
||||
'rows' => 6,
|
||||
]),
|
||||
FormField::editor('text', [
|
||||
'label' => 'Treść',
|
||||
'toolbar' => 'MyTool',
|
||||
'height' => 300,
|
||||
]),
|
||||
]),
|
||||
];
|
||||
|
||||
$actions = [
|
||||
FormAction::save(
|
||||
'/admin/banners/save/' . ($isNew ? '' : 'id=' . $bannerId),
|
||||
'/admin/banners/list/'
|
||||
),
|
||||
FormAction::cancel('/admin/banners/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getFormId(),
|
||||
$isNew ? 'Nowy baner' : 'Edycja banera',
|
||||
$banner,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
'/admin/banners/save/' . ($isNew ? '' : 'id=' . $bannerId),
|
||||
'/admin/banners/list/',
|
||||
true,
|
||||
['id' => $bannerId],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca identyfikator formularza
|
||||
*/
|
||||
private function getFormId(): string
|
||||
{
|
||||
return 'banner-edit';
|
||||
}
|
||||
}
|
||||
31
autoload/admin/Controllers/DashboardController.php
Normal file
31
autoload/admin/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Dashboard\DashboardRepository;
|
||||
use Domain\ShopStatus\ShopStatusRepository;
|
||||
|
||||
class DashboardController
|
||||
{
|
||||
private DashboardRepository $repository;
|
||||
private ShopStatusRepository $statusesRepository;
|
||||
|
||||
public function __construct( DashboardRepository $repository, ShopStatusRepository $statusesRepository )
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->statusesRepository = $statusesRepository;
|
||||
}
|
||||
|
||||
public function main_view(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'dashboard/main-view', [
|
||||
'last_orders' => $this->repository->lastOrders(),
|
||||
'order_statuses' => $this->statusesRepository->allStatuses(),
|
||||
'sales' => $this->repository->last24MonthsSales(),
|
||||
'best_sales_products' => $this->repository->bestSalesProducts(),
|
||||
'most_view_products' => $this->repository->mostViewedProducts(),
|
||||
'sales_grid' => $this->repository->salesGrid(),
|
||||
'summary_sales' => $this->repository->summarySales(),
|
||||
'summary_orders' => $this->repository->summaryOrders(),
|
||||
] );
|
||||
}
|
||||
}
|
||||
256
autoload/admin/Controllers/DictionariesController.php
Normal file
256
autoload/admin/Controllers/DictionariesController.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Dictionaries\DictionariesRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class DictionariesController
|
||||
{
|
||||
private DictionariesRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(DictionariesRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'text'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'text',
|
||||
'label' => 'Tekst',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'id'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$text = trim((string)($item['text'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'text' => '<a href="/admin/dictionaries/edit/id=' . $id . '">' . htmlspecialchars($text, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/dictionaries/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/dictionaries/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrana jednostke miary?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'text', 'sort_key' => 'text', 'label' => 'Tekst', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/dictionaries/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/dictionaries/edit/',
|
||||
'Dodaj jednostke miary'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('dictionaries/units-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$unitId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$unit = $this->repository->find($unitId) ?? ['id' => 0, 'languages' => []];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getFormId()]);
|
||||
}
|
||||
|
||||
$viewModel = $this->buildFormViewModel($unit, $languages, $validationErrors);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('dictionaries/unit-edit', ['form' => $viewModel]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode($legacyValues, true);
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania jednostki miary wystapil blad.'];
|
||||
|
||||
if (is_array($values)) {
|
||||
$savedId = $this->repository->save([
|
||||
'id' => (int)($values['id'] ?? 0),
|
||||
'text' => (array)($values['text'] ?? []),
|
||||
]);
|
||||
if ($savedId) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Jednostka miary zostala zapisana.', 'id' => $savedId];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$unitId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$unit = $this->repository->find($unitId) ?? ['id' => 0, 'languages' => []];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$viewModel = $this->buildFormViewModel($unit, $languages);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->getFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$data['id'] = $unitId ?: null;
|
||||
|
||||
$savedId = $this->repository->save($data);
|
||||
if ($savedId) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Jednostka miary zostala zapisana.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Blad podczas zapisywania do bazy.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
$unitId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($this->repository->delete($unitId)) {
|
||||
\Shared\Helpers\Helpers::alert('Jednostka miary zostala usunieta.');
|
||||
}
|
||||
|
||||
header('Location: /admin/dictionaries/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $unit, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$unitId = (int)($unit['id'] ?? 0);
|
||||
$isNew = $unitId <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $unitId,
|
||||
'languages' => [],
|
||||
];
|
||||
|
||||
if (isset($unit['languages']) && is_array($unit['languages'])) {
|
||||
foreach ($unit['languages'] as $langId => $translation) {
|
||||
$data['languages'][(string)$langId] = [
|
||||
'text' => (string)($translation['text'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$tabs = [
|
||||
new FormTab('content', 'Tresc', 'fa-file'),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::langSection('translations', 'content', [
|
||||
FormField::text('text', [
|
||||
'label' => 'Tekst',
|
||||
]),
|
||||
]),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/dictionaries/save/' . ($isNew ? '' : ('id=' . $unitId));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/dictionaries/list/'),
|
||||
FormAction::cancel('/admin/dictionaries/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getFormId(),
|
||||
$isNew ? 'Nowa jednostka miary' : 'Edycja jednostki miary',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/dictionaries/list/',
|
||||
true,
|
||||
['id' => $unitId],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function getFormId(): string
|
||||
{
|
||||
return 'dictionaries-unit-edit';
|
||||
}
|
||||
}
|
||||
46
autoload/admin/Controllers/FilemanagerController.php
Normal file
46
autoload/admin/Controllers/FilemanagerController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
class FilemanagerController
|
||||
{
|
||||
private const RFM_KEY_TTL = 1200; // 20 min
|
||||
private const FILEMANAGER_DIALOG_PATH = '/libraries/filemanager-9.14.2/dialog.php';
|
||||
|
||||
public function draw(): string
|
||||
{
|
||||
$akey = $this->ensureFilemanagerAccessKey();
|
||||
$filemanagerUrl = $this->buildFilemanagerUrl($akey);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('filemanager/filemanager', [
|
||||
'filemanager_url' => $filemanagerUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
private function ensureFilemanagerAccessKey(): string
|
||||
{
|
||||
$expiresAt = (int)($_SESSION['rfm_akey_expires'] ?? 0);
|
||||
$existingKey = trim((string)($_SESSION['rfm_akey'] ?? ''));
|
||||
|
||||
if ($existingKey !== '' && $expiresAt >= time()) {
|
||||
$_SESSION['rfm_akey_expires'] = time() + self::RFM_KEY_TTL;
|
||||
return $existingKey;
|
||||
}
|
||||
|
||||
try {
|
||||
$newKey = bin2hex(random_bytes(16));
|
||||
} catch (\Throwable $e) {
|
||||
$newKey = sha1(uniqid('rfm', true));
|
||||
}
|
||||
|
||||
$_SESSION['rfm_akey'] = $newKey;
|
||||
$_SESSION['rfm_akey_expires'] = time() + self::RFM_KEY_TTL;
|
||||
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
private function buildFilemanagerUrl(string $akey): string
|
||||
{
|
||||
return self::FILEMANAGER_DIALOG_PATH . '?akey=' . rawurlencode($akey);
|
||||
}
|
||||
}
|
||||
|
||||
178
autoload/admin/Controllers/IntegrationsController.php
Normal file
178
autoload/admin/Controllers/IntegrationsController.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Integrations\IntegrationsRepository;
|
||||
|
||||
class IntegrationsController
|
||||
{
|
||||
private IntegrationsRepository $repository;
|
||||
|
||||
public function __construct( IntegrationsRepository $repository )
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function apilo_settings(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'integrations/apilo-settings', [
|
||||
'settings' => $this->repository->getSettings( 'apilo' ),
|
||||
'apilo_status' => $this->repository->apiloIntegrationStatus(),
|
||||
] );
|
||||
}
|
||||
|
||||
public function apilo_settings_save(): void
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawien wystapil blad. Prosze sprobowac ponownie.' ];
|
||||
$fieldId = \Shared\Helpers\Helpers::get( 'field_id' );
|
||||
$value = \Shared\Helpers\Helpers::get( 'value' );
|
||||
|
||||
if ( $this->repository->saveSetting( 'apilo', $fieldId, $value ) ) {
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostaly zapisane.', 'value' => $value ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function apilo_authorization(): void
|
||||
{
|
||||
$settings = $this->repository->getSettings( 'apilo' );
|
||||
|
||||
if ( $this->repository->apiloAuthorize(
|
||||
(string)($settings['client-id'] ?? ''),
|
||||
(string)($settings['client-secret'] ?? ''),
|
||||
(string)($settings['authorization-code'] ?? '')
|
||||
) ) {
|
||||
echo json_encode( [ 'status' => 'ok', 'msg' => 'Autoryzacja przebiegla pomyslnie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$status = $this->repository->apiloIntegrationStatus();
|
||||
$message = trim( (string)($status['message'] ?? '') );
|
||||
if ( $message === '' ) {
|
||||
$message = 'Podczas autoryzacji wystapil blad. Prosze sprawdzic dane i sprobowac ponownie.';
|
||||
} else {
|
||||
$message = 'Autoryzacja nieudana. ' . $message;
|
||||
}
|
||||
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => $message ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function get_platform_list(): void
|
||||
{
|
||||
$this->fetchApiloListWithFeedback( 'platform', 'Liste platform' );
|
||||
}
|
||||
|
||||
public function get_status_types_list(): void
|
||||
{
|
||||
$this->fetchApiloListWithFeedback( 'status', 'Liste statusow' );
|
||||
}
|
||||
|
||||
public function get_carrier_account_list(): void
|
||||
{
|
||||
$this->fetchApiloListWithFeedback( 'carrier', 'Liste kont przewoznikow' );
|
||||
}
|
||||
|
||||
public function get_payment_types_list(): void
|
||||
{
|
||||
$this->fetchApiloListWithFeedback( 'payment', 'Liste metod platnosci' );
|
||||
}
|
||||
|
||||
public function apilo_create_product(): void
|
||||
{
|
||||
$productId = (int) \Shared\Helpers\Helpers::get( 'product_id' );
|
||||
$result = $this->repository->apiloCreateProduct( $productId );
|
||||
|
||||
\Shared\Helpers\Helpers::alert( (string)($result['message'] ?? 'Wystapil blad podczas tworzenia produktu w Apilo.') );
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function apilo_product_search(): void
|
||||
{
|
||||
$productId = (int) \Shared\Helpers\Helpers::get( 'product_id' );
|
||||
$sku = $this->repository->getProductSku( $productId );
|
||||
|
||||
if ( !$sku ) {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podany produkt nie posiada kodu SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode( $this->repository->apiloProductSearch( $sku ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function apilo_product_select_save(): void
|
||||
{
|
||||
if ( $this->repository->linkProduct( (int) \Shared\Helpers\Helpers::get( 'product_id' ), \Shared\Helpers\Helpers::get( 'apilo_product_id' ), \Shared\Helpers\Helpers::get( 'apilo_product_name' ) ) ) {
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
} else {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas zapisywania produktu wystapil blad. Prosze sprobowac ponownie.' ] );
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function apilo_product_select_delete(): void
|
||||
{
|
||||
if ( $this->repository->unlinkProduct( (int) \Shared\Helpers\Helpers::get( 'product_id' ) ) ) {
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
} else {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas usuwania produktu wystapil blad. Prosze sprobowac ponownie.' ] );
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function shoppro_settings(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'integrations/shoppro-settings', [
|
||||
'settings' => $this->repository->getSettings( 'shoppro' ),
|
||||
] );
|
||||
}
|
||||
|
||||
public function shoppro_settings_save(): void
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawien wystapil blad. Prosze sprobowac ponownie.' ];
|
||||
$fieldId = \Shared\Helpers\Helpers::get( 'field_id' );
|
||||
$value = \Shared\Helpers\Helpers::get( 'value' );
|
||||
|
||||
if ( $this->repository->saveSetting( 'shoppro', $fieldId, $value ) ) {
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostaly zapisane.', 'value' => $value ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function shoppro_product_import(): void
|
||||
{
|
||||
$productId = (int) \Shared\Helpers\Helpers::get( 'product_id' );
|
||||
$result = $this->repository->shopproImportProduct( $productId );
|
||||
|
||||
\Shared\Helpers\Helpers::alert( (string)($result['message'] ?? 'Wystapil blad podczas importu produktu.') );
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function fetchApiloListWithFeedback( string $type, string $label ): void
|
||||
{
|
||||
$result = $this->repository->apiloFetchListResult( $type );
|
||||
|
||||
if ( !empty( $result['success'] ) ) {
|
||||
$count = (int)($result['count'] ?? 0);
|
||||
\Shared\Helpers\Helpers::alert( $label . ' zostala pobrana. Liczba rekordow: ' . $count . '.' );
|
||||
} else {
|
||||
$details = trim( (string)($result['message'] ?? 'Nieznany blad.') );
|
||||
\Shared\Helpers\Helpers::alert(
|
||||
'Nie udalo sie pobrac ' . strtolower( $label ) . '. '
|
||||
. $details
|
||||
. ' Co zrobic: sprawdz konfiguracje Apilo, wykonaj autoryzacje i ponow pobranie listy.'
|
||||
);
|
||||
}
|
||||
|
||||
header( 'Location: /admin/integrations/apilo_settings/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
560
autoload/admin/Controllers/LanguagesController.php
Normal file
560
autoload/admin/Controllers/LanguagesController.php
Normal file
@@ -0,0 +1,560 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class LanguagesController
|
||||
{
|
||||
private LanguagesRepository $repository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(LanguagesRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['o', 'name', 'status', 'start'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Jezyk',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'start',
|
||||
'label' => 'Domyslny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- domyslny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'o'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (string)($item['id'] ?? '');
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'start' => ((int)($item['start'] ?? 0) === 1) ? '<span class="text-system">tak</span>' : 'nie',
|
||||
'status' => ((int)($item['status'] ?? 0) === 1) ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'name' => '<a href="/admin/languages/language_edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/languages/language_edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/languages/language_delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany jezyk?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'start', 'sort_key' => 'start', 'label' => 'Domyslny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Jezyk', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/languages/view_list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/languages/language_edit/',
|
||||
'Dodaj jezyk'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('languages/languages-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function language_edit(): string
|
||||
{
|
||||
$languageId = trim((string)\Shared\Helpers\Helpers::get('id'));
|
||||
$language = $this->repository->languageDetails($languageId) ?? [];
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getLanguageFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getLanguageFormId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('languages/language-edit', [
|
||||
'form' => $this->buildLanguageFormViewModel($language, $this->repository->maxOrder(), $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function language_save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania jezyka wystapil blad.'];
|
||||
|
||||
if (is_array($values)) {
|
||||
$savedId = $this->repository->saveLanguage(
|
||||
(string)($values['id'] ?? ''),
|
||||
(string)($values['name'] ?? ''),
|
||||
$values['status'] ?? 0,
|
||||
$values['start'] ?? 0,
|
||||
(int)($values['o'] ?? 0)
|
||||
);
|
||||
if ($savedId) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Jezyk zostal zapisany.', 'id' => $savedId];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$languageId = trim((string)\Shared\Helpers\Helpers::get('id'));
|
||||
$language = $this->repository->languageDetails($languageId) ?? [];
|
||||
$viewModel = $this->buildLanguageFormViewModel($language, $this->repository->maxOrder());
|
||||
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->getLanguageFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$requestId = strtolower(trim((string)\Shared\Helpers\Helpers::get('id')));
|
||||
$idFromData = strtolower(trim((string)($data['id'] ?? '')));
|
||||
$id = $idFromData !== '' ? $idFromData : $requestId;
|
||||
if (!preg_match('/^[a-z]{2}$/', $id)) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['id' => 'ID jezyka musi miec 2 litery (np. pl, en).'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$savedId = $this->repository->saveLanguage(
|
||||
$id,
|
||||
trim((string)($data['name'] ?? '')),
|
||||
$data['status'] ?? 0,
|
||||
$data['start'] ?? 0,
|
||||
(int)($data['o'] ?? 0)
|
||||
);
|
||||
|
||||
if ($savedId) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Jezyk zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania jezyka wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function language_delete(): void
|
||||
{
|
||||
if ($this->repository->deleteLanguage((string)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Jezyk zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/languages/view_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function translation_list(): string
|
||||
{
|
||||
$sortableColumns = ['text', 'id'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'text',
|
||||
'label' => 'Tekst',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'text'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listTranslationsForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$text = trim((string)($item['text'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'text' => '<a href="/admin/languages/translation_edit/id=' . $id . '">' . htmlspecialchars($text, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/languages/translation_edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/languages/translation_delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrane tlumaczenie?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'text', 'sort_key' => 'text', 'label' => 'Tekst', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/languages/translation_list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/languages/translation_edit/',
|
||||
'Dodaj tlumaczenie'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('languages/translations-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function translation_edit(): string
|
||||
{
|
||||
$translationId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$translation = $this->repository->translationDetails($translationId) ?? [];
|
||||
$languages = $this->repository->languagesList();
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getTranslationFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getTranslationFormId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('languages/translation-edit', [
|
||||
'form' => $this->buildTranslationFormViewModel($translation, $languages, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function translation_save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania tlumaczenia wystapil blad.'];
|
||||
|
||||
if (is_array($values)) {
|
||||
$languagesMap = $this->extractLegacyTranslations($values, $this->repository->languagesList());
|
||||
$savedId = $this->repository->saveTranslation(
|
||||
(int)($values['id'] ?? 0),
|
||||
(string)($values['text'] ?? ''),
|
||||
$languagesMap
|
||||
);
|
||||
if ($savedId) {
|
||||
$this->clearLanguageSessions($this->repository->languagesList());
|
||||
$response = ['status' => 'ok', 'msg' => 'Tlumaczenie zostalo zapisane.', 'id' => $savedId];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$translationId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$translation = $this->repository->translationDetails($translationId) ?? [];
|
||||
$languages = $this->repository->languagesList();
|
||||
$viewModel = $this->buildTranslationFormViewModel($translation, $languages);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->getTranslationFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$languagesMap = [];
|
||||
foreach ($languages as $language) {
|
||||
$langId = (string)($language['id'] ?? '');
|
||||
$key = 'lang_' . $langId;
|
||||
$languagesMap[$langId] = (string)($data[$key] ?? '');
|
||||
}
|
||||
|
||||
$savedId = $this->repository->saveTranslation(
|
||||
$translationId,
|
||||
(string)($data['text'] ?? ''),
|
||||
$languagesMap
|
||||
);
|
||||
|
||||
if ($savedId) {
|
||||
$this->clearLanguageSessions($languages);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Tlumaczenie zostalo zapisane.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania tlumaczenia wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function translation_delete(): void
|
||||
{
|
||||
if ($this->repository->deleteTranslation((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Tlumaczenie zostalo usuniete.');
|
||||
}
|
||||
|
||||
header('Location: /admin/languages/translation_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildLanguageFormViewModel(array $language, int $maxOrder, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$languageId = strtolower(trim((string)($language['id'] ?? '')));
|
||||
$isNew = $languageId === '';
|
||||
|
||||
$data = [
|
||||
'id' => $languageId,
|
||||
'name' => (string)($language['name'] ?? ''),
|
||||
'status' => (int)($language['status'] ?? 0),
|
||||
'start' => (int)($language['start'] ?? 0),
|
||||
'o' => (int)($language['o'] ?? ($maxOrder + 1)),
|
||||
];
|
||||
|
||||
$fields = [];
|
||||
if ($isNew) {
|
||||
$fields[] = FormField::text('id', [
|
||||
'label' => 'ID (2 znaki)',
|
||||
'required' => true,
|
||||
'attributes' => ['maxlength' => 2],
|
||||
]);
|
||||
}
|
||||
|
||||
$fields[] = FormField::text('name', [
|
||||
'label' => 'Jezyk',
|
||||
'required' => true,
|
||||
]);
|
||||
$fields[] = FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
]);
|
||||
$fields[] = FormField::switch('start', [
|
||||
'label' => 'Domyslny',
|
||||
]);
|
||||
$fields[] = FormField::hidden('o', $data['o']);
|
||||
|
||||
$actionUrl = '/admin/languages/language_save/' . ($isNew ? '' : ('id=' . $languageId));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/languages/view_list/'),
|
||||
FormAction::cancel('/admin/languages/view_list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getLanguageFormId(),
|
||||
$isNew ? 'Nowy jezyk' : 'Edycja jezyka',
|
||||
$data,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/languages/view_list/',
|
||||
true,
|
||||
$isNew ? [] : ['id' => $languageId],
|
||||
null,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function buildTranslationFormViewModel(array $translation, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$translationId = (int)($translation['id'] ?? 0);
|
||||
$isNew = $translationId <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $translationId,
|
||||
'text' => (string)($translation['text'] ?? ''),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::text('text', [
|
||||
'label' => 'Tekst',
|
||||
'required' => true,
|
||||
]),
|
||||
];
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$langId = (string)($language['id'] ?? '');
|
||||
$fieldName = 'lang_' . $langId;
|
||||
$data[$fieldName] = (string)($translation[$langId] ?? '');
|
||||
$fields[] = FormField::text($fieldName, [
|
||||
'label' => (string)($language['name'] ?? $langId),
|
||||
]);
|
||||
}
|
||||
|
||||
$actionUrl = '/admin/languages/translation_save/' . ($isNew ? '' : ('id=' . $translationId));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/languages/translation_list/'),
|
||||
FormAction::cancel('/admin/languages/translation_list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getTranslationFormId(),
|
||||
$isNew ? 'Nowe tlumaczenie' : 'Edycja tlumaczenia',
|
||||
$data,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/languages/translation_list/',
|
||||
true,
|
||||
$isNew ? [] : ['id' => $translationId],
|
||||
null,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function extractLegacyTranslations(array $values, array $languages): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($languages as $language) {
|
||||
$langId = (string)($language['id'] ?? '');
|
||||
$result[$langId] = (string)($values[$langId] ?? '');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function clearLanguageSessions(array $languages): void
|
||||
{
|
||||
foreach ($languages as $language) {
|
||||
if (!isset($language['id'])) {
|
||||
continue;
|
||||
}
|
||||
\Shared\Helpers\Helpers::delete_session('lang-' . (string)$language['id']);
|
||||
}
|
||||
}
|
||||
|
||||
private function getLanguageFormId(): string
|
||||
{
|
||||
return 'languages-language-edit';
|
||||
}
|
||||
|
||||
private function getTranslationFormId(): string
|
||||
{
|
||||
return 'languages-translation-edit';
|
||||
}
|
||||
}
|
||||
172
autoload/admin/Controllers/LayoutsController.php
Normal file
172
autoload/admin/Controllers/LayoutsController.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Layouts\LayoutsRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
|
||||
class LayoutsController
|
||||
{
|
||||
private LayoutsRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
|
||||
public function __construct(LayoutsRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['name', 'status', 'categories_default'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Szablon domyslny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- domyslny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'categories_default',
|
||||
'label' => 'Domyslny (kategorie)',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- kategorie -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'name' => '<a href="/admin/layouts/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'status' => ((int)($item['status'] ?? 0) === 1) ? '<span class="text-system">tak</span>' : 'nie',
|
||||
'categories_default' => ((int)($item['categories_default'] ?? 0) === 1) ? '<span class="text-system">tak</span>' : 'nie',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/layouts/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/layouts/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany szablon?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Szablon domyslny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'categories_default', 'sort_key' => 'categories_default', 'label' => 'Domyslny (kategorie)', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/layouts/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/layouts/edit/',
|
||||
'Dodaj szablon'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('layouts/layouts-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('layouts/layout-edit', [
|
||||
'layout' => $this->repository->find((int)\Shared\Helpers\Helpers::get('id')),
|
||||
'menus' => $this->repository->menusWithPages(),
|
||||
'categories' => $this->repository->categoriesTree(),
|
||||
'dlang' => $this->languagesRepository->defaultLanguageId(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania szablonu wystapil blad. Prosze sprobowac ponownie.'];
|
||||
$values = json_decode((string)\Shared\Helpers\Helpers::get('values'), true);
|
||||
|
||||
if (is_array($values)) {
|
||||
$id = $this->repository->save($values);
|
||||
if (!empty($id)) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Szablon zostal zapisany.', 'id' => $id];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Szablon zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/layouts/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
514
autoload/admin/Controllers/NewsletterController.php
Normal file
514
autoload/admin/Controllers/NewsletterController.php
Normal file
@@ -0,0 +1,514 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Newsletter\NewsletterRepository;
|
||||
use Domain\Newsletter\NewsletterPreviewRenderer;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class NewsletterController
|
||||
{
|
||||
private NewsletterRepository $repository;
|
||||
private NewsletterPreviewRenderer $previewRenderer;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(NewsletterRepository $repository, NewsletterPreviewRenderer $previewRenderer)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->previewRenderer = $previewRenderer;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return $this->emails_list();
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function emails_list(): string
|
||||
{
|
||||
$sortableColumns = ['email', 'status'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'email',
|
||||
'label' => 'Email',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Potwierdzony',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- potwierdzony -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'email'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listSubscribersForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$email = trim((string)($item['email'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'email' => htmlspecialchars($email, ENT_QUOTES, 'UTF-8'),
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/newsletter/email_delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany adres email?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'email', 'sort_key' => 'email', 'label' => 'Email', 'sortable' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Potwierdzony', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/newsletter/emails_list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('newsletter/emails-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function email_delete(): void
|
||||
{
|
||||
if ($this->repository->deleteSubscriber((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Adres email zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/newsletter/emails_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
$this->email_delete();
|
||||
}
|
||||
|
||||
public function prepare(): string
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert('Funkcjonalnosc "Wysylka - przygotowanie" jest tymczasowo wylaczona.');
|
||||
header('Location: /admin/newsletter/emails_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function preview(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function send(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert('Funkcjonalnosc "Wysylka - przygotowanie" jest tymczasowo wylaczona.');
|
||||
header('Location: /admin/newsletter/emails_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function settings(): string
|
||||
{
|
||||
$settings = $this->repository->getSettings();
|
||||
$validationErrors = $_SESSION['form_errors'][$this->settingsFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->settingsFormId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('newsletter/settings', [
|
||||
'form' => $this->buildSettingsFormViewModel($settings, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function settings_save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
if (!is_array($values)) {
|
||||
echo json_encode(['status' => 'error', 'msg' => 'Nieprawidlowe dane formularza.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->repository->saveSettings($values);
|
||||
\Shared\Helpers\Helpers::alert('Ustawienia zostaly zapisane.');
|
||||
|
||||
echo json_encode(['status' => 'ok', 'msg' => 'Ustawienia zostaly zapisane.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$viewModel = $this->buildSettingsFormViewModel($this->repository->getSettings());
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->settingsFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->repository->saveSettings($result['data']);
|
||||
\Shared\Helpers\Helpers::alert('Ustawienia zostaly zapisane.');
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Ustawienia zostaly zapisane.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function email_templates_user(): string
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert('Funkcjonalnosc "Szablony uzytkownika" jest tymczasowo wylaczona.');
|
||||
header('Location: /admin/newsletter/email_templates_admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function email_templates_admin(): string
|
||||
{
|
||||
$viewModel = $this->templatesListViewModel();
|
||||
return \Shared\Tpl\Tpl::view('newsletter/email-templates-admin', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function email_template_edit(): string
|
||||
{
|
||||
$template = $this->repository->templateDetails((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if (!is_array($template) || (int)($template['is_admin'] ?? 0) !== 1) {
|
||||
\Shared\Helpers\Helpers::alert('Dostepne sa tylko szablony administracyjne.');
|
||||
header('Location: /admin/newsletter/email_templates_admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$formId = $this->templateFormId((int)$template['id']);
|
||||
$validationErrors = $_SESSION['form_errors'][$formId] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$formId]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('newsletter/email-template-edit', [
|
||||
'form' => $this->buildTemplateFormViewModel($template, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function template_save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania wystapil blad.'];
|
||||
|
||||
if (is_array($values)) {
|
||||
$templateId = (int)($values['id'] ?? 0);
|
||||
$template = $this->repository->templateDetails($templateId);
|
||||
|
||||
if (is_array($template) && (int)($template['is_admin'] ?? 0) === 1) {
|
||||
$id = $this->repository->saveTemplate(
|
||||
$templateId,
|
||||
(string)($values['name'] ?? ''),
|
||||
(string)($values['text'] ?? '')
|
||||
);
|
||||
if ($id) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Zmiany zostaly zapisane.', 'id' => $id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$template = $this->repository->templateDetails((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if (!is_array($template) || (int)($template['is_admin'] ?? 0) !== 1) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Dostepne sa tylko szablony administracyjne.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$form = $this->buildTemplateFormViewModel($template);
|
||||
$result = $this->formHandler->handleSubmit($form, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->templateFormId((int)$template['id'])] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$id = $this->repository->saveTemplate(
|
||||
(int)($template['id'] ?? 0),
|
||||
(string)($data['name'] ?? ''),
|
||||
(string)($data['text'] ?? '')
|
||||
);
|
||||
|
||||
if ($id) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $id,
|
||||
'message' => 'Zmiany zostaly zapisane.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function email_template_delete(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::alert('Usuwanie szablonow uzytkownika jest tymczasowo wylaczone.');
|
||||
header('Location: /admin/newsletter/email_templates_admin/');
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildSettingsFormViewModel(array $settings, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$data = [
|
||||
'newsletter_header' => (string)($settings['newsletter_header'] ?? ''),
|
||||
'newsletter_footer' => (string)($settings['newsletter_footer'] ?? ''),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::editor('newsletter_header', [
|
||||
'label' => 'Naglowek',
|
||||
'height' => 150,
|
||||
]),
|
||||
FormField::editor('newsletter_footer', [
|
||||
'label' => 'Stopka',
|
||||
'height' => 150,
|
||||
]),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/newsletter/settings_save/';
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/newsletter/settings/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->settingsFormId(),
|
||||
'Edycja ustawien newslettera',
|
||||
$data,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/newsletter/settings/',
|
||||
true,
|
||||
[],
|
||||
null,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function buildTemplateFormViewModel(array $template, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$templateId = (int)($template['id'] ?? 0);
|
||||
$isAdminTemplate = (int)($template['is_admin'] ?? 0) === 1;
|
||||
$isNew = $templateId <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $templateId,
|
||||
'name' => (string)($template['name'] ?? ''),
|
||||
'text' => (string)($template['text'] ?? ''),
|
||||
];
|
||||
|
||||
$nameAttrs = [];
|
||||
if ($isAdminTemplate) {
|
||||
$nameAttrs['readonly'] = 'readonly';
|
||||
}
|
||||
|
||||
$fields = [
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'required' => true,
|
||||
'attributes' => $nameAttrs,
|
||||
]),
|
||||
FormField::editor('text', [
|
||||
'label' => 'Tresc',
|
||||
'required' => true,
|
||||
'height' => 350,
|
||||
]),
|
||||
];
|
||||
|
||||
$backUrl = '/admin/newsletter/email_templates_admin/';
|
||||
$actionUrl = '/admin/newsletter/template_save/' . ($isNew ? '' : ('id=' . $templateId));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, $backUrl),
|
||||
FormAction::cancel($backUrl),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->templateFormId($templateId),
|
||||
'Edycja szablonu newslettera',
|
||||
$data,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
$backUrl,
|
||||
true,
|
||||
['id' => $templateId],
|
||||
null,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function templatesListViewModel(): PaginatedTableViewModel
|
||||
{
|
||||
$sortableColumns = ['name'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$basePath = '/admin/newsletter/email_templates_admin/';
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listTemplatesForAdmin(
|
||||
true,
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
|
||||
$actions = [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/newsletter/email_template_edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
];
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'name' => '<a href="/admin/newsletter/email_template_edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'_actions' => $actions,
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
return new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
$basePath,
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
}
|
||||
|
||||
private function settingsFormId(): string
|
||||
{
|
||||
return 'newsletter-settings-edit';
|
||||
}
|
||||
|
||||
private function templateFormId(int $templateId): string
|
||||
{
|
||||
return 'newsletter-template-edit-' . $templateId;
|
||||
}
|
||||
}
|
||||
660
autoload/admin/Controllers/PagesController.php
Normal file
660
autoload/admin/Controllers/PagesController.php
Normal file
@@ -0,0 +1,660 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Pages\PagesRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use Domain\Layouts\LayoutsRepository;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class PagesController
|
||||
{
|
||||
private PagesRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private LayoutsRepository $layoutsRepository;
|
||||
|
||||
public function __construct(
|
||||
PagesRepository $repository,
|
||||
LanguagesRepository $languagesRepository,
|
||||
LayoutsRepository $layoutsRepository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->layoutsRepository = $layoutsRepository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('pages/pages-list', [
|
||||
'menus' => $this->repository->menusWithPages(),
|
||||
'cookie_pages' => $this->cookieState('cookie_pages'),
|
||||
'cookie_menus' => $this->cookieState('cookie_menus'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function browseList(): string
|
||||
{
|
||||
$menus = $this->repository->menusWithPages();
|
||||
$defaultLanguage = $this->languagesRepository->defaultLanguageId();
|
||||
|
||||
foreach ($menus as $index => $menu) {
|
||||
$menus[$index]['pages'] = $this->withPreviewUrls($menu['pages'] ?? [], $defaultLanguage);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('pages/pages-browse-list', [
|
||||
'menus' => $menus,
|
||||
'modal' => \Shared\Helpers\Helpers::get('modal'),
|
||||
'cookie_pages' => $this->cookieState('cookie_pages'),
|
||||
'cookie_menus' => $this->cookieState('cookie_menus'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function pagesUrlBrowser(): string
|
||||
{
|
||||
return $this->browseList();
|
||||
}
|
||||
|
||||
public function menuEdit(): string
|
||||
{
|
||||
$menu = $this->repository->menuDetails((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
|
||||
return \Shared\Tpl\Tpl::view('pages/menu-edit', [
|
||||
'form' => $this->buildMenuFormViewModel($menu),
|
||||
]);
|
||||
}
|
||||
|
||||
public function menuSave(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania menu wystapil blad. Prosze sprobowac ponownie.'];
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
|
||||
if (is_array($values) && $this->repository->menuSave(
|
||||
(int)($values['id'] ?? 0),
|
||||
(string)($values['name'] ?? ''),
|
||||
$values['status'] ?? 0
|
||||
)) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Menu zostalo zapisane.'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$menuId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$name = trim((string)\Shared\Helpers\Helpers::get('name'));
|
||||
$status = \Shared\Helpers\Helpers::get('status');
|
||||
|
||||
if ($name === '') {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['name' => 'Pole "Nazwa" jest wymagane.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($this->repository->menuSave($menuId, $name, $status)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Menu zostalo zapisane.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania menu wystapil blad. Prosze sprobowac ponownie.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $menu
|
||||
*/
|
||||
private function buildMenuFormViewModel(array $menu): FormEditViewModel
|
||||
{
|
||||
$menuId = (int)($menu['id'] ?? 0);
|
||||
$actionUrl = '/admin/pages/menuSave/' . ($menuId > 0 ? ('id=' . $menuId) : '');
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $menuId),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'required' => true,
|
||||
'attributes' => ['class' => 'require'],
|
||||
'value' => (string)($menu['name'] ?? ''),
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywne',
|
||||
'value' => (int)($menu['status'] ?? 0) === 1,
|
||||
]),
|
||||
];
|
||||
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/pages/list/'),
|
||||
FormAction::cancel('/admin/pages/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'pages-menu-edit',
|
||||
'Zapisz menu',
|
||||
$menu,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/pages/list/',
|
||||
true,
|
||||
['id' => $menuId]
|
||||
);
|
||||
}
|
||||
|
||||
public function menuDelete(): void
|
||||
{
|
||||
if ($this->repository->menuDelete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::set_message('Menu zostało usunięte.');
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::alert('Podczas usuwania menu wystąpił błąd. Aby usunąć menu, nie może ono posiadać przypiętych stron.');
|
||||
}
|
||||
|
||||
header('Location: /admin/pages/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$page = $this->repository->pageDetails((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$parentId = (int)\Shared\Helpers\Helpers::get('pid');
|
||||
$menuId = (int)\Shared\Helpers\Helpers::get('menu_id');
|
||||
$menus = $this->repository->menusList();
|
||||
$layouts = $this->layoutsRepository->listAll();
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('pages/page-edit', [
|
||||
'form' => $this->buildPageFormViewModel(
|
||||
$page,
|
||||
$parentId,
|
||||
$menuId,
|
||||
$menus,
|
||||
$layouts,
|
||||
$languages
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania strony wystapil blad. Prosze sprobowac ponownie.'];
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
|
||||
if (is_array($values)) {
|
||||
$id = $this->repository->pageSave($values);
|
||||
if (!empty($id)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Strona zostala zapisana.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $_POST;
|
||||
if (!isset($data['id']) || $data['id'] === '') {
|
||||
$data['id'] = (int)\Shared\Helpers\Helpers::get('id');
|
||||
}
|
||||
if (!isset($data['parent_id']) || $data['parent_id'] === '') {
|
||||
$data['parent_id'] = (int)\Shared\Helpers\Helpers::get('pid');
|
||||
}
|
||||
if ((!isset($data['menu_id']) || $data['menu_id'] === '') && (int)\Shared\Helpers\Helpers::get('menu_id') > 0) {
|
||||
$data['menu_id'] = (int)\Shared\Helpers\Helpers::get('menu_id');
|
||||
}
|
||||
|
||||
$savedId = $this->repository->pageSave($data);
|
||||
if (!empty($savedId)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Strona zostala zapisana.',
|
||||
'id' => (int)$savedId,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania strony wystapil blad. Prosze sprobowac ponownie.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $page
|
||||
* @param array<int, array<string, mixed>> $menus
|
||||
* @param array<int, array<string, mixed>> $layouts
|
||||
* @param array<int, array<string, mixed>> $languages
|
||||
*/
|
||||
private function buildPageFormViewModel(
|
||||
array $page,
|
||||
int $parentId,
|
||||
int $menuId,
|
||||
array $menus,
|
||||
array $layouts,
|
||||
array $languages
|
||||
): FormEditViewModel {
|
||||
$pageId = (int)($page['id'] ?? 0);
|
||||
$isNew = $pageId <= 0;
|
||||
|
||||
$resolvedParentId = $pageId > 0 ? (int)($page['parent_id'] ?? 0) : $parentId;
|
||||
$resolvedMenuId = $pageId > 0 ? (int)($page['menu_id'] ?? 0) : $menuId;
|
||||
|
||||
$menuOptions = [];
|
||||
foreach ($menus as $menu) {
|
||||
$id = (int)($menu['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
}
|
||||
$menuOptions[$id] = (string)($menu['name'] ?? '');
|
||||
}
|
||||
|
||||
$layoutOptions = ['' => '---- szablon ----'];
|
||||
foreach ($layouts as $layout) {
|
||||
$id = (int)($layout['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
continue;
|
||||
}
|
||||
$layoutOptions[$id] = (string)($layout['name'] ?? '');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $pageId,
|
||||
'parent_id' => $resolvedParentId > 0 ? $resolvedParentId : '',
|
||||
'menu_id' => $resolvedMenuId > 0 ? $resolvedMenuId : '',
|
||||
'page_type' => (int)($page['page_type'] ?? 0),
|
||||
'sort_type' => (int)($page['sort_type'] ?? 0),
|
||||
'layout_id' => (int)($page['layout_id'] ?? 0),
|
||||
'articles_limit' => (int)($page['articles_limit'] ?? 2),
|
||||
'show_title' => (int)($page['show_title'] ?? 1),
|
||||
'status' => (int)($page['status'] ?? 1),
|
||||
'start' => (int)($page['start'] ?? 0),
|
||||
'category_id' => (int)($page['category_id'] ?? 0),
|
||||
'languages' => [],
|
||||
];
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$langId = (string)($language['id'] ?? '');
|
||||
if ($langId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation = is_array($page['languages'][$langId] ?? null) ? $page['languages'][$langId] : [];
|
||||
$data['languages'][$langId] = [
|
||||
'title' => (string)($translation['title'] ?? ''),
|
||||
'link' => (string)($translation['link'] ?? ''),
|
||||
'seo_link' => (string)($translation['seo_link'] ?? ''),
|
||||
'page_title' => (string)($translation['page_title'] ?? ''),
|
||||
'meta_title' => (string)($translation['meta_title'] ?? ''),
|
||||
'meta_description' => (string)($translation['meta_description'] ?? ''),
|
||||
'meta_keywords' => (string)($translation['meta_keywords'] ?? ''),
|
||||
'noindex' => (int)($translation['noindex'] ?? 0),
|
||||
'canonical' => (string)($translation['canonical'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
$tabs = [
|
||||
new FormTab('content', 'Tresc', 'fa-file'),
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('seo', 'SEO', 'fa-globe'),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $pageId),
|
||||
FormField::hidden('parent_id', $resolvedParentId > 0 ? $resolvedParentId : ''),
|
||||
FormField::langSection('page_content', 'content', [
|
||||
FormField::text('title', [
|
||||
'label' => 'Nazwa strony',
|
||||
'required' => true,
|
||||
]),
|
||||
]),
|
||||
FormField::select('menu_id', [
|
||||
'label' => 'Menu',
|
||||
'tab' => 'settings',
|
||||
'options' => $menuOptions,
|
||||
'value' => $resolvedMenuId > 0 ? $resolvedMenuId : '',
|
||||
]),
|
||||
FormField::select('page_type', [
|
||||
'label' => 'Typ strony',
|
||||
'tab' => 'settings',
|
||||
'options' => $this->repository->pageTypes(),
|
||||
'attributes' => ['id' => 'page_type'],
|
||||
'value' => (int)($page['page_type'] ?? 0),
|
||||
]),
|
||||
FormField::langSection('page_links', 'settings', [
|
||||
FormField::text('link', [
|
||||
'label' => 'Link',
|
||||
]),
|
||||
]),
|
||||
FormField::text('category_id', [
|
||||
'label' => 'ID kategorii',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['id' => 'category_id'],
|
||||
'value' => (int)($page['category_id'] ?? 0),
|
||||
]),
|
||||
FormField::select('sort_type', [
|
||||
'label' => 'Sortowanie artykulow',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['id' => 'sort_type'],
|
||||
'options' => $this->repository->sortTypes(),
|
||||
'value' => (int)($page['sort_type'] ?? 0),
|
||||
]),
|
||||
FormField::select('layout_id', [
|
||||
'label' => 'Szablon',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['id' => 'layout_id'],
|
||||
'options' => $layoutOptions,
|
||||
'value' => (int)($page['layout_id'] ?? 0),
|
||||
]),
|
||||
FormField::text('articles_limit', [
|
||||
'label' => 'Liczba artykulow na stronie',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['id' => 'articles_limit'],
|
||||
'value' => (int)($page['articles_limit'] ?? 2),
|
||||
]),
|
||||
FormField::switch('show_title', [
|
||||
'label' => 'Pokaz tytul',
|
||||
'tab' => 'settings',
|
||||
'value' => (int)($page['show_title'] ?? 0) === 1 || $isNew,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywna',
|
||||
'tab' => 'settings',
|
||||
'value' => (int)($page['status'] ?? 0) === 1 || $isNew,
|
||||
]),
|
||||
FormField::switch('start', [
|
||||
'label' => 'Strona startowa',
|
||||
'tab' => 'settings',
|
||||
'value' => (int)($page['start'] ?? 0) === 1,
|
||||
]),
|
||||
FormField::langSection('page_seo', 'seo', [
|
||||
FormField::text('seo_link', [
|
||||
'label' => 'Link SEO',
|
||||
'attributes' => [
|
||||
'icon_content' => 'generuj',
|
||||
'icon_js' => 'generateSeoLinkForButton(\'{lang}\')',
|
||||
],
|
||||
]),
|
||||
FormField::text('page_title', [
|
||||
'label' => 'Tytul strony (h1)',
|
||||
]),
|
||||
FormField::text('meta_title', [
|
||||
'label' => 'Meta title',
|
||||
]),
|
||||
FormField::textarea('meta_description', [
|
||||
'label' => 'Meta description',
|
||||
]),
|
||||
FormField::textarea('meta_keywords', [
|
||||
'label' => 'Meta keywords',
|
||||
]),
|
||||
FormField::select('noindex', [
|
||||
'label' => 'Blokuj indeksacje',
|
||||
'options' => [0 => 'nie', 1 => 'tak'],
|
||||
]),
|
||||
FormField::text('canonical', [
|
||||
'label' => 'Rel canonical',
|
||||
]),
|
||||
]),
|
||||
FormField::custom('page_edit_script', '
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
function toggleByType() {
|
||||
var pageType = String($(\'#page_type\').val() || \'\');
|
||||
var $links = $(\'#languages-page_links\');
|
||||
var $category = $(\'#category_id\').closest(\'.form-group\');
|
||||
var $articlesLimit = $(\'#articles_limit\').closest(\'.form-group\');
|
||||
var $sortType = $(\'#sort_type\').closest(\'.form-group\');
|
||||
|
||||
if (pageType === \'0\' || pageType === \'1\' || pageType === \'2\') {
|
||||
$articlesLimit.show();
|
||||
$sortType.show();
|
||||
$links.addClass(\'hide\');
|
||||
$category.hide();
|
||||
} else if (pageType === \'3\') {
|
||||
$links.removeClass(\'hide\');
|
||||
$articlesLimit.hide();
|
||||
$sortType.hide();
|
||||
$category.hide();
|
||||
} else if (pageType === \'5\') {
|
||||
$category.show();
|
||||
$links.addClass(\'hide\');
|
||||
$articlesLimit.hide();
|
||||
$sortType.hide();
|
||||
} else {
|
||||
$links.addClass(\'hide\');
|
||||
$category.hide();
|
||||
$articlesLimit.hide();
|
||||
$sortType.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function generateSeoLink(langId, title) {
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: \'POST\',
|
||||
cache: false,
|
||||
url: \'/admin/pages/generateSeoLink/\',
|
||||
data: {
|
||||
title: title,
|
||||
page_id: ' . $pageId . '
|
||||
},
|
||||
success: function (data) {
|
||||
var response = data;
|
||||
if (typeof data === \'string\') {
|
||||
try { response = JSON.parse(data); } catch (e) { response = null; }
|
||||
}
|
||||
if (response && response.status === \'ok\') {
|
||||
$(\'#seo_link_\' + langId).val(response.seo_link || \'\');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.generateSeoLinkForButton = function (langId) {
|
||||
var title = $(\'#title_\' + langId).val() || \'\';
|
||||
generateSeoLink(langId, title);
|
||||
};
|
||||
|
||||
$(function () {
|
||||
$(\'#page_type\').on(\'change\', toggleByType);
|
||||
toggleByType();
|
||||
|
||||
$(\'[id^=title_]\').on(\'blur\', function () {
|
||||
var fieldId = $(this).attr(\'id\') || \'\';
|
||||
var langId = fieldId.replace(/^title_/, \'\');
|
||||
if (!langId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $seo = $(\'#seo_link_\' + langId);
|
||||
if ($seo.length && !$seo.val()) {
|
||||
generateSeoLink(langId, $(this).val());
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>', [
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/pages/save/' . ($pageId > 0 ? ('id=' . $pageId) : '');
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/pages/list/'),
|
||||
FormAction::cancel('/admin/pages/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'pages-page-edit',
|
||||
'Edycja strony',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/pages/list/',
|
||||
true,
|
||||
[
|
||||
'id' => $pageId,
|
||||
'parent_id' => $resolvedParentId > 0 ? $resolvedParentId : '',
|
||||
],
|
||||
$languages
|
||||
);
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->pageDelete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::set_message('Strona zostala usunieta.');
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::alert('Podczas usuwania strony wystapil blad. Aby usunac strone nie moze ona posiadac przypietych podstron.');
|
||||
}
|
||||
|
||||
header('Location: /admin/pages/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function pageArticles(): string
|
||||
{
|
||||
$pageId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
return \Shared\Tpl\Tpl::view('pages/page-articles', [
|
||||
'page_id' => $pageId,
|
||||
'articles' => $this->repository->pageArticles($pageId),
|
||||
]);
|
||||
}
|
||||
|
||||
public function savePagesOrder(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania kolejnosci stron wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->savePagesOrder((int)\Shared\Helpers\Helpers::get('menu_id'), \Shared\Helpers\Helpers::get('pages'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function saveArticlesOrder(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania kolejnosci wyswietlania artykulow wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
if ($this->repository->saveArticlesOrder((int)\Shared\Helpers\Helpers::get('page_id'), \Shared\Helpers\Helpers::get('articles'))) {
|
||||
$response = ['status' => 'ok'];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function generateSeoLink(): void
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas generowania pola "seo link" wystapil blad. Prosze sprobowac ponownie.'];
|
||||
|
||||
$seoLink = $this->repository->generateSeoLink(
|
||||
(string)\Shared\Helpers\Helpers::get('title'),
|
||||
(int)\Shared\Helpers\Helpers::get('page_id'),
|
||||
(int)\Shared\Helpers\Helpers::get('article_id'),
|
||||
(int)\Shared\Helpers\Helpers::get('category_id')
|
||||
);
|
||||
|
||||
if ($seoLink !== '') {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'seo_link' => $seoLink,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function cookieMenus(): void
|
||||
{
|
||||
$this->repository->toggleCookieValue('cookie_menus', (int)\Shared\Helpers\Helpers::get('menu_id'));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function cookiePages(): void
|
||||
{
|
||||
$this->repository->toggleCookieValue('cookie_pages', (int)\Shared\Helpers\Helpers::get('page_id'));
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $pages
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function withPreviewUrls(array $pages, string $defaultLanguage): array
|
||||
{
|
||||
foreach ($pages as $index => $page) {
|
||||
$pageId = (int)($page['id'] ?? 0);
|
||||
$languages = is_array($page['languages'] ?? null) ? $page['languages'] : [];
|
||||
|
||||
$previewUrls = [];
|
||||
foreach ($languages as $languageRow) {
|
||||
$langId = (string)($languageRow['lang_id'] ?? '');
|
||||
if ($langId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$previewUrls[$langId] = $this->repository->pageUrlPreview(
|
||||
$pageId,
|
||||
$langId,
|
||||
(string)($languageRow['title'] ?? ''),
|
||||
(string)($languageRow['seo_link'] ?? ''),
|
||||
$defaultLanguage
|
||||
);
|
||||
}
|
||||
|
||||
$page['preview_urls'] = $previewUrls;
|
||||
$page['subpages'] = $this->withPreviewUrls($page['subpages'] ?? [], $defaultLanguage);
|
||||
$pages[$index] = $page;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function cookieState(string $cookieName): array
|
||||
{
|
||||
if (empty($_COOKIE[$cookieName])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = @unserialize((string)$_COOKIE[$cookieName], ['allowed_classes' => false]);
|
||||
if (!is_array($decoded)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$state = [];
|
||||
foreach ($decoded as $key => $value) {
|
||||
$state[(int)$key] = (int)$value;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
|
||||
165
autoload/admin/Controllers/ProductArchiveController.php
Normal file
165
autoload/admin/Controllers/ProductArchiveController.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Product\ProductRepository;
|
||||
|
||||
class ProductArchiveController
|
||||
{
|
||||
private ProductRepository $productRepository;
|
||||
|
||||
public function __construct(ProductRepository $productRepository)
|
||||
{
|
||||
$this->productRepository = $productRepository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'price_brutto', 'price_brutto_promo', 'quantity'];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'phrase',
|
||||
'label' => 'Nazwa / EAN / SKU',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'id',
|
||||
[10, 15, 25, 50, 100],
|
||||
10
|
||||
);
|
||||
|
||||
$result = $this->productRepository->listArchivedForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$listRequest['sortDir'],
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$sku = trim((string)($item['sku'] ?? ''));
|
||||
$ean = trim((string)($item['ean'] ?? ''));
|
||||
$imageSrc = trim((string)($item['image_src'] ?? ''));
|
||||
$imageAlt = trim((string)($item['image_alt'] ?? ''));
|
||||
$priceBrutto = (string)($item['price_brutto'] ?? '');
|
||||
$priceBruttoPromo = (string)($item['price_brutto_promo'] ?? '');
|
||||
$quantity = (int)($item['quantity'] ?? 0);
|
||||
$combinations = (int)($item['combinations'] ?? 0);
|
||||
|
||||
if ($imageSrc === '') {
|
||||
$imageSrc = '/admin/layout/images/no-image.png';
|
||||
} elseif (!preg_match('#^(https?:)?//#i', $imageSrc) && strpos($imageSrc, '/') !== 0) {
|
||||
$imageSrc = '/' . ltrim($imageSrc, '/');
|
||||
}
|
||||
|
||||
$categories = trim((string)$this->productRepository->productCategoriesText($id));
|
||||
$categoriesHtml = '';
|
||||
if ($categories !== '') {
|
||||
$categoriesHtml = '<small class="text-muted product-categories">'
|
||||
. htmlspecialchars($categories, ENT_QUOTES, 'UTF-8')
|
||||
. '</small>';
|
||||
}
|
||||
|
||||
$skuEanParts = [];
|
||||
if ($sku !== '') {
|
||||
$skuEanParts[] = 'SKU: ' . htmlspecialchars($sku, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
if ($ean !== '') {
|
||||
$skuEanParts[] = 'EAN: ' . htmlspecialchars($ean, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$skuEanHtml = '';
|
||||
if (!empty($skuEanParts)) {
|
||||
$skuEanHtml = '<small class="text-muted product-categories">' . implode(', ', $skuEanParts) . '</small>';
|
||||
}
|
||||
|
||||
$productCell = '<div class="product-image product-archive-thumb-wrap">'
|
||||
. '<img src="' . htmlspecialchars($imageSrc, ENT_QUOTES, 'UTF-8') . '" alt="' . htmlspecialchars($imageAlt, ENT_QUOTES, 'UTF-8') . '" '
|
||||
. 'data-preview-src="' . htmlspecialchars($imageSrc, ENT_QUOTES, 'UTF-8') . '" '
|
||||
. 'class="img-responsive product-archive-thumb-image js-product-archive-thumb-preview" loading="lazy">'
|
||||
. '</div>'
|
||||
. '<div class="product-name">'
|
||||
. '<a href="/admin/shop_product/product_edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>'
|
||||
. '</div>'
|
||||
. $categoriesHtml
|
||||
. $skuEanHtml;
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'product' => $productCell,
|
||||
'price_brutto' => $priceBrutto !== '' ? $priceBrutto : '-',
|
||||
'price_brutto_promo' => $priceBruttoPromo !== '' ? $priceBruttoPromo : '-',
|
||||
'quantity' => (string)$quantity,
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Przywroc',
|
||||
'url' => '/admin/product_archive/unarchive/product_id=' . $id,
|
||||
'class' => 'btn btn-xs btn-success',
|
||||
'confirm' => 'Na pewno chcesz przywrocic wybrany produkt z archiwum?',
|
||||
'confirm_ok' => 'Przywroc',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'product', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'price_brutto', 'sort_key' => 'price_brutto', 'label' => 'Cena', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'price_brutto_promo', 'sort_key' => 'price_brutto_promo', 'label' => 'Cena promocyjna', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'quantity', 'sort_key' => 'quantity', 'label' => 'Stan MG', 'class' => 'text-center', 'sortable' => true]
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/product_archive/list/',
|
||||
'Brak danych w tabeli.',
|
||||
null,
|
||||
null,
|
||||
'product-archive/products-list-custom-script'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('product-archive/products-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function unarchive(): void
|
||||
{
|
||||
if ( $this->productRepository->unarchive( (int) \Shared\Helpers\Helpers::get( 'product_id' ) ) )
|
||||
\Shared\Helpers\Helpers::alert( 'Produkt został przywrócony z archiwum.' );
|
||||
else
|
||||
\Shared\Helpers\Helpers::alert( 'Podczas przywracania produktu z archiwum wystąpił błąd. Proszę spróbować ponownie' );
|
||||
|
||||
header( 'Location: /admin/product_archive/list/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
297
autoload/admin/Controllers/ScontainersController.php
Normal file
297
autoload/admin/Controllers/ScontainersController.php
Normal file
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use Domain\Scontainers\ScontainersRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class ScontainersController
|
||||
{
|
||||
private ScontainersRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(ScontainersRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'title', 'status'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'title',
|
||||
'label' => 'Tytul',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'id'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'DESC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$title = trim((string)($item['title'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'title' => '<a href="/admin/scontainers/edit/id=' . $id . '">' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'code' => '[KONTENER:' . $id . ']',
|
||||
'status' => ((int)($item['status'] ?? 0) === 1) ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/scontainers/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/scontainers/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany kontener?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'title', 'sort_key' => 'title', 'label' => 'Tytul', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'code', 'label' => 'Kod', 'sortable' => false],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/scontainers/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/scontainers/edit/',
|
||||
'Dodaj kontener'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('scontainers/containers-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$container = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$validationErrors = $_SESSION['form_errors'][$this->formId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->formId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('scontainers/container-edit', [
|
||||
'form' => $this->buildFormViewModel($container, $languages, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function container_edit(): string
|
||||
{
|
||||
return $this->edit();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania kontenera wystapil blad.'];
|
||||
|
||||
if (is_array($values)) {
|
||||
$savedId = $this->repository->save($values);
|
||||
if (!empty($savedId)) {
|
||||
$response = ['status' => 'ok', 'msg' => 'Kontener zostal zapisany.', 'id' => $savedId];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$container = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$form = $this->buildFormViewModel($container, $languages);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($form, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->formId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$savedId = $this->repository->save([
|
||||
'id' => (int)($data['id'] ?? 0),
|
||||
'status' => $data['status'] ?? 0,
|
||||
'show_title' => $data['show_title'] ?? 0,
|
||||
'translations' => $data['translations'] ?? [],
|
||||
]);
|
||||
|
||||
if ($savedId) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Kontener zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania kontenera wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function container_save(): void
|
||||
{
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Kontener zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/scontainers/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function container_delete(): void
|
||||
{
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $container, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$id = (int)($container['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'status' => (int)($container['status'] ?? 1),
|
||||
'show_title' => (int)($container['show_title'] ?? 0),
|
||||
'languages' => is_array($container['languages'] ?? null) ? $container['languages'] : [],
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::langSection('translations', 'content', [
|
||||
FormField::text('title', [
|
||||
'label' => 'Tytul',
|
||||
]),
|
||||
FormField::editor('text', [
|
||||
'label' => 'Tresc',
|
||||
'height' => 300,
|
||||
]),
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::switch('show_title', [
|
||||
'label' => 'Pokaz tytul',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('content', 'Tresc', 'fa-file'),
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/scontainers/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/scontainers/list/'),
|
||||
FormAction::cancel('/admin/scontainers/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->formId(),
|
||||
'Edycja kontenera statycznego',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/scontainers/list/',
|
||||
true,
|
||||
[],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function formId(): string
|
||||
{
|
||||
return 'scontainers-container-edit';
|
||||
}
|
||||
}
|
||||
|
||||
536
autoload/admin/Controllers/SettingsController.php
Normal file
536
autoload/admin/Controllers/SettingsController.php
Normal file
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use Domain\Settings\SettingsRepository;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
/**
|
||||
* Kontroler ustawien w panelu administratora.
|
||||
*/
|
||||
class SettingsController
|
||||
{
|
||||
private SettingsRepository $settingsRepository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(SettingsRepository $settingsRepository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->settingsRepository = $settingsRepository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyszczenie cache.
|
||||
*/
|
||||
public function clearCache(): void
|
||||
{
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::delete_dir('../thumbs/');
|
||||
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
||||
if ($redis) {
|
||||
$redis->flushAll();
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::alert('Cache został wyczyszczony.');
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
|
||||
header('Location: /admin/dashboard/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyszczenie cache (AJAX).
|
||||
*/
|
||||
public function clearCacheAjax(): void
|
||||
{
|
||||
try {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::delete_dir('../thumbs/');
|
||||
|
||||
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
||||
if ($redis) {
|
||||
$redis->flushAll();
|
||||
}
|
||||
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
|
||||
echo json_encode(['status' => 'success', 'message' => 'Cache został wyczyszczony.']);
|
||||
} catch (\Exception $e) {
|
||||
echo json_encode(['status' => 'error', 'message' => 'Błąd podczas czyszczenia cache: ' . $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Globalna wyszukiwarka admin (produkty + zamowienia) - AJAX.
|
||||
*/
|
||||
public function globalSearchAjax(): void
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$phrase = trim((string)\Shared\Helpers\Helpers::get('q'));
|
||||
if ($phrase === '' || mb_strlen($phrase) < 2) {
|
||||
echo json_encode([
|
||||
'status' => 'ok',
|
||||
'items' => [],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$phrase = mb_substr($phrase, 0, 120);
|
||||
$phraseNormalized = preg_replace('/\s+/', ' ', $phrase);
|
||||
$phraseNormalized = trim((string)$phraseNormalized);
|
||||
$like = '%' . $phrase . '%';
|
||||
$likeNormalized = '%' . $phraseNormalized . '%';
|
||||
|
||||
$items = [];
|
||||
$defaultLang = (string)$this->languagesRepository->defaultLanguage();
|
||||
|
||||
try {
|
||||
$productStmt = $mdb->query(
|
||||
'SELECT '
|
||||
. 'p.id, p.ean, p.sku, p.parent_id, psl.name '
|
||||
. 'FROM pp_shop_products AS p '
|
||||
. 'LEFT JOIN pp_shop_products_langs AS psl ON psl.product_id = p.id AND psl.lang_id = :lang_id '
|
||||
. 'WHERE '
|
||||
. '(p.ean LIKE :q1 OR p.sku LIKE :q2 OR psl.name LIKE :q3) '
|
||||
. 'AND p.archive != 1 '
|
||||
. 'ORDER BY p.id DESC '
|
||||
. 'LIMIT 15',
|
||||
[
|
||||
':lang_id' => $defaultLang,
|
||||
':q1' => $like,
|
||||
':q2' => $like,
|
||||
':q3' => $like,
|
||||
]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$productStmt = false;
|
||||
}
|
||||
|
||||
$productRows = $productStmt ? $productStmt->fetchAll() : [];
|
||||
if (is_array($productRows)) {
|
||||
foreach ($productRows as $row) {
|
||||
$productId = (int)($row['id'] ?? 0);
|
||||
if ($productId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = trim((string)($row['name'] ?? ''));
|
||||
if ($name === '') {
|
||||
$name = 'Produkt #' . $productId;
|
||||
}
|
||||
|
||||
$meta = [];
|
||||
$sku = trim((string)($row['sku'] ?? ''));
|
||||
$ean = trim((string)($row['ean'] ?? ''));
|
||||
if ($sku !== '') {
|
||||
$meta[] = 'SKU: ' . $sku;
|
||||
}
|
||||
if ($ean !== '') {
|
||||
$meta[] = 'EAN: ' . $ean;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'type' => 'product',
|
||||
'title' => $name,
|
||||
'subtitle' => implode(' | ', $meta),
|
||||
'url' => '/admin/shop_product/product_edit/id=' . $productId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$orderStmt = $mdb->query(
|
||||
'SELECT '
|
||||
. 'id, number, client_name, client_surname, client_email, client_phone '
|
||||
. 'FROM pp_shop_orders '
|
||||
. 'WHERE '
|
||||
. '('
|
||||
. 'number LIKE :q1 '
|
||||
. 'OR client_email LIKE :q2 '
|
||||
. 'OR client_name LIKE :q3 '
|
||||
. 'OR client_surname LIKE :q4 '
|
||||
. 'OR client_phone LIKE :q5 '
|
||||
. "OR CONCAT_WS(' ', TRIM(client_name), TRIM(client_surname)) LIKE :q6 "
|
||||
. "OR CONCAT_WS(' ', TRIM(client_surname), TRIM(client_name)) LIKE :q7 "
|
||||
. ') '
|
||||
. 'ORDER BY id DESC '
|
||||
. 'LIMIT 15',
|
||||
[
|
||||
':q1' => $like,
|
||||
':q2' => $like,
|
||||
':q3' => $like,
|
||||
':q4' => $like,
|
||||
':q5' => $like,
|
||||
':q6' => $likeNormalized,
|
||||
':q7' => $likeNormalized,
|
||||
]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$orderStmt = false;
|
||||
}
|
||||
|
||||
$orderRows = $orderStmt ? $orderStmt->fetchAll() : [];
|
||||
if (is_array($orderRows)) {
|
||||
foreach ($orderRows as $row) {
|
||||
$orderId = (int)($row['id'] ?? 0);
|
||||
if ($orderId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orderNumber = trim((string)($row['number'] ?? ''));
|
||||
$clientName = trim((string)($row['client_name'] ?? ''));
|
||||
$clientSurname = trim((string)($row['client_surname'] ?? ''));
|
||||
$clientEmail = trim((string)($row['client_email'] ?? ''));
|
||||
$clientPhone = trim((string)($row['client_phone'] ?? ''));
|
||||
|
||||
$title = $orderNumber !== '' ? 'Zamówienie ' . $orderNumber : 'Zamówienie #' . $orderId;
|
||||
$subtitleParts = [];
|
||||
$fullName = trim($clientName . ' ' . $clientSurname);
|
||||
if ($fullName !== '') {
|
||||
$subtitleParts[] = $fullName;
|
||||
}
|
||||
if ($clientEmail !== '') {
|
||||
$subtitleParts[] = $clientEmail;
|
||||
}
|
||||
if ($clientPhone !== '') {
|
||||
$subtitleParts[] = $clientPhone;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'type' => 'order',
|
||||
'title' => $title,
|
||||
'subtitle' => implode(' | ', $subtitleParts),
|
||||
'url' => '/admin/shop_order/order_details/order_id=' . $orderId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'ok',
|
||||
'items' => array_slice($items, 0, 20),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapis ustawien (AJAX).
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
// Kompatybilnosc wsteczna dla legacy gridEdit (values jako JSON).
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode($legacyValues, true);
|
||||
$result = $this->settingsRepository->saveSettings(is_array($values) ? $values : []);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$settings = $this->settingsRepository->getSettings();
|
||||
$viewModel = $this->buildFormViewModel($settings, $languages);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->getFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$values = $this->transformFormDataToSettings($result['data']);
|
||||
$saveResult = $this->settingsRepository->saveSettings($values);
|
||||
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
|
||||
echo json_encode([
|
||||
'success' => ($saveResult['status'] ?? '') === 'ok',
|
||||
'message' => $saveResult['msg'] ?? 'Ustawienia zostały zapisane.',
|
||||
'errors' => (($saveResult['status'] ?? '') === 'ok') ? [] : ['general' => ($saveResult['msg'] ?? 'Błąd zapisu.')],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widok ustawien.
|
||||
*/
|
||||
public function view(): string
|
||||
{
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$settings = $this->settingsRepository->getSettings();
|
||||
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getFormId()]);
|
||||
}
|
||||
|
||||
$viewModel = $this->buildFormViewModel($settings, $languages, $validationErrors);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('components/form-edit', ['form' => $viewModel]);
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $settings, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$data = $this->transformSettingsToFormData($settings, $languages);
|
||||
|
||||
$tabs = [
|
||||
new FormTab('contact', 'Dane kontaktowe', 'fa-paper-plane'),
|
||||
new FormTab('shop', 'Sklep', 'fa-dollar'),
|
||||
new FormTab('products', 'Produkty', 'fa-shopping-cart'),
|
||||
new FormTab('mail', 'Poczta', 'fa-envelope'),
|
||||
new FormTab('other', 'Pozostałe', 'fa-bars'),
|
||||
new FormTab('system', 'System', 'fa-cog'),
|
||||
new FormTab('conversions', 'Konwersje', 'fa-line-chart'),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::text('firm_name', [
|
||||
'label' => 'Nazwa firmy',
|
||||
'tab' => 'contact',
|
||||
]),
|
||||
FormField::editor('additional_info', [
|
||||
'label' => 'Dodatkowe informacje',
|
||||
'tab' => 'contact',
|
||||
'height' => 150,
|
||||
]),
|
||||
FormField::switch('google_maps', [
|
||||
'label' => 'Mapa',
|
||||
'tab' => 'contact',
|
||||
]),
|
||||
FormField::textarea('firm_adress', [
|
||||
'label' => 'Mapa - adres',
|
||||
'tab' => 'contact',
|
||||
]),
|
||||
|
||||
FormField::editor('shop_bank_account_info', [
|
||||
'label' => 'Dane do przelewu',
|
||||
'tab' => 'shop',
|
||||
'height' => 200,
|
||||
]),
|
||||
FormField::text('hotpay_api', [
|
||||
'label' => 'Klucz API HotPay',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::switch('tpay_sandbox', [
|
||||
'label' => 'Tpay.com - tryb sandbox',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::text('tpay_id', [
|
||||
'label' => 'Tpay.com ID',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::text('tpay_security_code', [
|
||||
'label' => 'Tpay.com - kod bezpieczeństwa',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::switch('przelewy24_sandbox', [
|
||||
'label' => 'Przelewy24.pl - tryb sandbox',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::text('przelewy24_merchant_id', [
|
||||
'label' => 'Przelewy24.pl - merchant ID',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::text('przelewy24_crc_key', [
|
||||
'label' => 'Przelewy24.pl - klucz CRC',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
FormField::text('free_delivery', [
|
||||
'label' => 'Darmowa dostawa od',
|
||||
'tab' => 'shop',
|
||||
'attributes' => ['class' => 'number-format'],
|
||||
]),
|
||||
FormField::text('orlen_paczka_map_token', [
|
||||
'label' => 'Orlen Paczka map token',
|
||||
'tab' => 'shop',
|
||||
]),
|
||||
|
||||
FormField::langSection('warehouse_messages', 'products', [
|
||||
FormField::text('warehouse_message_zero', [
|
||||
'label' => 'Komunikat gdy stan magazynowy równy 0',
|
||||
]),
|
||||
FormField::text('warehouse_message_nonzero', [
|
||||
'label' => 'Komunikat gdy stan magazynowy większy niż 0',
|
||||
]),
|
||||
]),
|
||||
|
||||
FormField::switch('contact_form', [
|
||||
'label' => 'Formularz kontaktowy',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
FormField::text('contact_email', [
|
||||
'label' => 'Email kontaktowy',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
FormField::text('email_host', [
|
||||
'label' => 'Email - host',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
FormField::text('email_port', [
|
||||
'label' => 'Email - port',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
FormField::text('email_login', [
|
||||
'label' => 'Email - login',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
FormField::text('email_password', [
|
||||
'label' => 'Email - hasło',
|
||||
'tab' => 'mail',
|
||||
]),
|
||||
|
||||
FormField::text('facebook_link', [
|
||||
'label' => 'Facebook link',
|
||||
'tab' => 'other',
|
||||
]),
|
||||
FormField::text('piksel', [
|
||||
'label' => 'Piksel Facebook',
|
||||
'tab' => 'other',
|
||||
]),
|
||||
FormField::textarea('statistic_code', [
|
||||
'label' => 'Kod statystyk',
|
||||
'tab' => 'other',
|
||||
'rows' => 10,
|
||||
]),
|
||||
FormField::textarea('htaccess', [
|
||||
'label' => 'Własne reguły htacess',
|
||||
'tab' => 'other',
|
||||
'rows' => 10,
|
||||
]),
|
||||
FormField::textarea('robots', [
|
||||
'label' => 'Własne reguły robots.txt',
|
||||
'tab' => 'other',
|
||||
'rows' => 10,
|
||||
]),
|
||||
|
||||
FormField::switch('update', [
|
||||
'label' => 'Aktualizacja',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::text('update_key', [
|
||||
'label' => 'Numer licencji',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::switch('devel', [
|
||||
'label' => 'Strona konstrukcyjna',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::switch('lazy_loading', [
|
||||
'label' => 'Lazy loading obrazów',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::switch('generate_webp', [
|
||||
'label' => 'Generowanie obrazków WEBP',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::switch('infinitescroll', [
|
||||
'label' => 'Infinitescroll',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::switch('htaccess_cache', [
|
||||
'label' => 'Htaccess cache',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
FormField::text('api_key', [
|
||||
'label' => 'Klucz API (ordersPRO)',
|
||||
'tab' => 'system',
|
||||
]),
|
||||
|
||||
FormField::text('google_tag_manager_id', [
|
||||
'label' => 'Google Tag Manager - ID',
|
||||
'tab' => 'conversions',
|
||||
]),
|
||||
FormField::textarea('own_gtm_js', [
|
||||
'label' => 'Własny kod GTM JS (bez tagu script)',
|
||||
'tab' => 'conversions',
|
||||
'rows' => 10,
|
||||
]),
|
||||
FormField::textarea('own_gtm_html', [
|
||||
'label' => 'Własny kod GTM HTML',
|
||||
'tab' => 'conversions',
|
||||
'rows' => 10,
|
||||
]),
|
||||
];
|
||||
|
||||
$actions = [
|
||||
FormAction::save('/admin/settings/save/', ''),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getFormId(),
|
||||
'Edycja ustawień',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
'/admin/settings/save/',
|
||||
null,
|
||||
false,
|
||||
[],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function getFormId(): string
|
||||
{
|
||||
return 'settings-edit';
|
||||
}
|
||||
|
||||
private function transformSettingsToFormData(array $settings, array $languages): array
|
||||
{
|
||||
$data = $settings;
|
||||
$data['languages'] = [];
|
||||
|
||||
foreach ($languages as $lang) {
|
||||
if (!($lang['status'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$langId = (string)$lang['id'];
|
||||
$data['languages'][$langId] = [
|
||||
'warehouse_message_zero' => $settings['warehouse_message_zero_' . $langId] ?? '',
|
||||
'warehouse_message_nonzero' => $settings['warehouse_message_nonzero_' . $langId] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function transformFormDataToSettings(array $data): array
|
||||
{
|
||||
if (!isset($data['warehouse_messages']) || !is_array($data['warehouse_messages'])) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data['warehouse_message_zero'] = [];
|
||||
$data['warehouse_message_nonzero'] = [];
|
||||
|
||||
foreach ($data['warehouse_messages'] as $langId => $langValues) {
|
||||
if (!is_array($langValues)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data['warehouse_message_zero'][$langId] = $langValues['warehouse_message_zero'] ?? '';
|
||||
$data['warehouse_message_nonzero'][$langId] = $langValues['warehouse_message_nonzero'] ?? '';
|
||||
}
|
||||
|
||||
unset($data['warehouse_messages']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
451
autoload/admin/Controllers/ShopAttributeController.php
Normal file
451
autoload/admin/Controllers/ShopAttributeController.php
Normal file
@@ -0,0 +1,451 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Attribute\AttributeRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopAttributeController
|
||||
{
|
||||
private AttributeRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
|
||||
public function __construct(
|
||||
AttributeRepository $repository,
|
||||
LanguagesRepository $languagesRepository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'o', 'name', 'type', 'status', 'values_count'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'o'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$type = (int)($item['type'] ?? 0);
|
||||
$order = (int)($item['o'] ?? 0);
|
||||
$valuesCount = (int)($item['values_count'] ?? 0);
|
||||
|
||||
$typeLabel = '-';
|
||||
if ($type === 0) {
|
||||
$typeLabel = 'tekst';
|
||||
} elseif ($type === 1) {
|
||||
$typeLabel = 'kolor';
|
||||
} elseif ($type === 2) {
|
||||
$typeLabel = 'wzor';
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'o' => $order,
|
||||
'name' => '<a href="/admin/shop_attribute/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'type' => htmlspecialchars($typeLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'values' => '<a href="/admin/shop_attribute/values/id=' . $id . '">edytuj wartosci</a>',
|
||||
'values_count' => $valuesCount,
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_attribute/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/shop_attribute/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrana ceche?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'o', 'sort_key' => 'o', 'label' => 'Kolejnosc', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'type', 'sort_key' => 'type', 'label' => 'Typ', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'values_count', 'sort_key' => 'values_count', 'label' => 'Ilosc wartosci', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'values', 'label' => 'Wartosci', 'class' => 'text-center', 'sortable' => false, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_attribute/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/shop_attribute/edit/',
|
||||
'Dodaj ceche'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-attribute/attributes-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$attribute = $this->repository->findAttribute((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-attribute/attribute-edit', [
|
||||
'form' => $this->buildFormViewModel($attribute, $languages),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania atrybutu wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
if (is_array($values)) {
|
||||
$id = $this->repository->saveAttribute($values);
|
||||
if (!empty($id)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Atrybut zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
if (empty($payload['id'])) {
|
||||
$routeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($routeId > 0) {
|
||||
$payload['id'] = $routeId;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $this->repository->saveAttribute($payload);
|
||||
if (!empty($id)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Atrybut zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania atrybutu wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->deleteAttribute((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Atrybut zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_attribute/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function values(): string
|
||||
{
|
||||
$attributeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($attributeId <= 0) {
|
||||
\Shared\Helpers\Helpers::alert('Nieprawidlowy identyfikator cechy.');
|
||||
header('Location: /admin/shop_attribute/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$attribute = $this->repository->findAttribute($attributeId);
|
||||
if ((int)($attribute['id'] ?? 0) <= 0) {
|
||||
\Shared\Helpers\Helpers::alert('Wybrana cecha nie zostala znaleziona.');
|
||||
header('Location: /admin/shop_attribute/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-attribute/values-edit', [
|
||||
'attribute' => $attribute,
|
||||
'values' => $this->repository->findValues($attributeId),
|
||||
'languages' => $languages,
|
||||
'defaultLanguageId' => $this->languagesRepository->defaultLanguageId(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function values_save(): void
|
||||
{
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania wartosci atrybutu wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
$attributeId = (int)\Shared\Helpers\Helpers::get('attribute_id');
|
||||
if ($attributeId <= 0) {
|
||||
$attributeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
}
|
||||
|
||||
$payloadRaw = \Shared\Helpers\Helpers::get('payload');
|
||||
$payload = json_decode((string)$payloadRaw, true);
|
||||
if (is_array($payload) && is_array($payload['rows'] ?? null) && $attributeId > 0) {
|
||||
$validationErrors = $this->validateValuesRows(
|
||||
$payload['rows'],
|
||||
$this->languagesRepository->defaultLanguageId()
|
||||
);
|
||||
|
||||
if (!empty($validationErrors)) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'msg' => $validationErrors[0],
|
||||
'errors' => $validationErrors,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$saved = $this->repository->saveValues($attributeId, ['rows' => $payload['rows']]);
|
||||
if ($saved) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Wartosci atrybutu zostaly zapisane.',
|
||||
'id' => (int)$attributeId,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$valuesRaw = \Shared\Helpers\Helpers::get('values');
|
||||
$values = json_decode((string)$valuesRaw, true);
|
||||
if (is_array($values) && $attributeId > 0) {
|
||||
$savedId = $this->repository->saveLegacyValues(
|
||||
$attributeId,
|
||||
is_array($values['name'] ?? null) ? $values['name'] : [],
|
||||
is_array($values['value'] ?? null) ? $values['value'] : [],
|
||||
is_array($values['ids'] ?? null) ? $values['ids'] : [],
|
||||
$values['default_value'] ?? '',
|
||||
is_array($values['impact_on_the_price'] ?? null) ? $values['impact_on_the_price'] : []
|
||||
);
|
||||
|
||||
if (!empty($savedId)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Wartosci atrybutu zostaly zapisane.',
|
||||
'id' => (int)$savedId,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function value_row_tpl(): void
|
||||
{
|
||||
$rowKey = trim((string)\Shared\Helpers\Helpers::get('row_key'));
|
||||
if ($rowKey === '') {
|
||||
$rowKey = 'new-' . time();
|
||||
}
|
||||
|
||||
$html = \Shared\Tpl\Tpl::view('shop-attribute/_partials/value-row', [
|
||||
'rowKey' => $rowKey,
|
||||
'value' => ['id' => 0, 'is_default' => 0, 'impact_on_the_price' => null, 'languages' => []],
|
||||
'languages' => $this->languagesRepository->languagesList(),
|
||||
'defaultLanguageId' => $this->languagesRepository->defaultLanguageId(),
|
||||
]);
|
||||
|
||||
echo $html;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $rows
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function validateValuesRows(array $rows, string $defaultLanguageId): array
|
||||
{
|
||||
$errors = [];
|
||||
if (empty($rows)) {
|
||||
return ['Dodaj co najmniej jedna wartosc cechy.'];
|
||||
}
|
||||
|
||||
$defaultCount = 0;
|
||||
foreach ($rows as $index => $row) {
|
||||
$rowNumber = $index + 1;
|
||||
if (!is_array($row)) {
|
||||
$errors[] = 'Nieprawidlowe dane wiersza nr ' . $rowNumber . '.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($row['is_default'])) {
|
||||
++$defaultCount;
|
||||
}
|
||||
|
||||
$translations = is_array($row['translations'] ?? null) ? $row['translations'] : [];
|
||||
$defaultLangData = is_array($translations[$defaultLanguageId] ?? null)
|
||||
? $translations[$defaultLanguageId]
|
||||
: [];
|
||||
$defaultName = trim((string)($defaultLangData['name'] ?? ''));
|
||||
if ($defaultName === '') {
|
||||
$errors[] = 'Wiersz nr ' . $rowNumber . ': nazwa w jezyku domyslnym jest wymagana.';
|
||||
}
|
||||
|
||||
$impact = trim((string)($row['impact_on_the_price'] ?? ''));
|
||||
if ($impact !== '' && !preg_match('/^-?[0-9]+([.,][0-9]{1,4})?$/', $impact)) {
|
||||
$errors[] = 'Wiersz nr ' . $rowNumber . ': nieprawidlowy format "wplyw na cene".';
|
||||
}
|
||||
}
|
||||
|
||||
if ($defaultCount !== 1) {
|
||||
$errors[] = 'Wybierz dokladnie jedna wartosc domyslna.';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $attribute, array $languages): FormEditViewModel
|
||||
{
|
||||
$id = (int)($attribute['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'status' => (int)($attribute['status'] ?? 1),
|
||||
'type' => (int)($attribute['type'] ?? 0),
|
||||
'o' => (int)($attribute['o'] ?? 0),
|
||||
'languages' => [],
|
||||
];
|
||||
|
||||
if (is_array($attribute['languages'] ?? null)) {
|
||||
foreach ($attribute['languages'] as $langId => $translation) {
|
||||
$data['languages'][(string)$langId] = [
|
||||
'name' => (string)($translation['name'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::langSection('attribute_content', 'content', [
|
||||
FormField::text('name', [
|
||||
'label' => 'Tytul',
|
||||
]),
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::select('type', [
|
||||
'label' => 'Typ',
|
||||
'tab' => 'settings',
|
||||
'options' => [
|
||||
0 => 'tekst',
|
||||
1 => 'kolor',
|
||||
2 => 'wzor',
|
||||
],
|
||||
]),
|
||||
FormField::number('o', [
|
||||
'label' => 'Kolejnosc',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('content', 'Tresc', 'fa-file'),
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_attribute/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_attribute/list/'),
|
||||
FormAction::cancel('/admin/shop_attribute/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-attribute-edit',
|
||||
$isNew ? 'Nowa cecha' : 'Edycja cechy',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_attribute/list/',
|
||||
true,
|
||||
['id' => $id],
|
||||
$languages
|
||||
);
|
||||
}
|
||||
}
|
||||
164
autoload/admin/Controllers/ShopCategoryController.php
Normal file
164
autoload/admin/Controllers/ShopCategoryController.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?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 \Shared\Tpl\Tpl::view('shop-category/categories-list', [
|
||||
'categories' => $this->repository->subcategories(null),
|
||||
'level' => 0,
|
||||
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return $this->view_list();
|
||||
}
|
||||
|
||||
public function category_edit(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('shop-category/category-edit', [
|
||||
'category' => $this->repository->categoryDetails(\Shared\Helpers\Helpers::get('id')),
|
||||
'pid' => \Shared\Helpers\Helpers::get('pid'),
|
||||
'languages' => $this->languagesRepository->languagesList(),
|
||||
'sort_types' => $this->repository->sortTypes(),
|
||||
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||
]);
|
||||
}
|
||||
|
||||
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)\Shared\Helpers\Helpers::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(\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::set_message('Kategoria została usunięta.');
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::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 \Shared\Tpl\Tpl::view('shop-category/category-products', [
|
||||
'category_id' => \Shared\Helpers\Helpers::get('id'),
|
||||
'products' => $this->repository->categoryProducts((int)\Shared\Helpers\Helpers::get('id')),
|
||||
]);
|
||||
}
|
||||
|
||||
public function products(): string
|
||||
{
|
||||
return $this->category_products();
|
||||
}
|
||||
|
||||
public function category_url_browser(): void
|
||||
{
|
||||
echo \Shared\Tpl\Tpl::view('shop-category/category-browse-list', [
|
||||
'categories' => $this->repository->subcategories(null),
|
||||
'level' => 0,
|
||||
'dlang' => $this->languagesRepository->defaultLanguage(),
|
||||
]);
|
||||
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( \Shared\Helpers\Helpers::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( \Shared\Helpers\Helpers::get( 'category_id' ), \Shared\Helpers\Helpers::get( 'products' ) ) ) {
|
||||
$response = [ 'status' => 'ok' ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function cookie_categories(): void
|
||||
{
|
||||
$categoryId = (string) \Shared\Helpers\Helpers::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;
|
||||
}
|
||||
}
|
||||
222
autoload/admin/Controllers/ShopClientsController.php
Normal file
222
autoload/admin/Controllers/ShopClientsController.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Client\ClientRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
|
||||
class ShopClientsController
|
||||
{
|
||||
private ClientRepository $repository;
|
||||
|
||||
public function __construct(ClientRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = [
|
||||
'client_name',
|
||||
'client_surname',
|
||||
'client_email',
|
||||
'client_phone',
|
||||
'client_city',
|
||||
'total_orders',
|
||||
'total_spent',
|
||||
'client_type',
|
||||
];
|
||||
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Imie',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'surname',
|
||||
'label' => 'Nazwisko',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'email',
|
||||
'label' => 'E-mail',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'client_type',
|
||||
'label' => 'Typ klienta',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- typ klienta -',
|
||||
'registered' => 'Zarejestrowany',
|
||||
'guest' => 'Gosc',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'client_surname'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
|
||||
foreach ($result['items'] as $item) {
|
||||
$name = trim((string)($item['client_name'] ?? ''));
|
||||
$surname = trim((string)($item['client_surname'] ?? ''));
|
||||
$email = trim((string)($item['client_email'] ?? ''));
|
||||
$params = [
|
||||
'name' => $name,
|
||||
'surname' => $surname,
|
||||
'email' => $email,
|
||||
];
|
||||
$detailsUrl = '/admin/shop_clients/details/?' . http_build_query($params);
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'client_type' => ((int)($item['is_registered'] ?? 0) === 1) ? 'Zarejestrowany' : 'Gosc',
|
||||
'full_name' => htmlspecialchars($surname, ENT_QUOTES, 'UTF-8') . ' ' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8'),
|
||||
'client_email' => $email,
|
||||
'client_phone' => (string)($item['client_phone'] ?? ''),
|
||||
'client_city' => (string)($item['client_city'] ?? ''),
|
||||
'total_spent' => number_format((float)($item['total_spent'] ?? 0), 2, '.', ' ') . ' zl',
|
||||
'total_orders' => '<a href="' . htmlspecialchars($detailsUrl, ENT_QUOTES, 'UTF-8') . '">' . (int)($item['total_orders'] ?? 0) . '</a>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Zobacz zamowienia',
|
||||
'url' => $detailsUrl,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'client_type', 'sort_key' => 'client_type', 'label' => 'Typ klienta', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'full_name', 'label' => 'Nazwisko, imie', 'sortable' => false, 'raw' => true],
|
||||
['key' => 'client_email', 'sort_key' => 'client_email', 'label' => 'Email', 'sortable' => true],
|
||||
['key' => 'client_phone', 'sort_key' => 'client_phone', 'label' => 'Telefon', 'sortable' => true],
|
||||
['key' => 'client_city', 'sort_key' => 'client_city', 'label' => 'Miasto', 'sortable' => true],
|
||||
['key' => 'total_spent', 'sort_key' => 'total_spent', 'label' => 'Wartosc zamowien', 'class' => 'text-right', 'sortable' => true],
|
||||
['key' => 'total_orders', 'sort_key' => 'total_orders', 'label' => 'Ilosc zamowien', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_clients/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-clients/view-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function details(): string
|
||||
{
|
||||
$name = (string)\Shared\Helpers\Helpers::get('name');
|
||||
$surname = (string)\Shared\Helpers\Helpers::get('surname');
|
||||
$email = (string)\Shared\Helpers\Helpers::get('email');
|
||||
|
||||
$ordersInfo = $this->repository->ordersForClient($name, $surname, $email);
|
||||
$totals = $this->repository->totalsForClient($name, $surname, $email);
|
||||
|
||||
$rows = [];
|
||||
$lp = 1;
|
||||
foreach ($ordersInfo as $order) {
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'date_order' => (string)($order['date_order'] ?? ''),
|
||||
'summary' => number_format((float)($order['summary'] ?? 0), 2, '.', ' ') . ' zl',
|
||||
'payment_method' => (string)($order['payment_method'] ?? ''),
|
||||
'transport' => (string)($order['transport'] ?? ''),
|
||||
'message' => (string)($order['message'] ?? ''),
|
||||
'_actions' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$ordersTable = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'date_order', 'label' => 'Data zamowienia', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'summary', 'label' => 'Wartosc', 'class' => 'text-right', 'sortable' => false],
|
||||
['key' => 'payment_method', 'label' => 'Typ platnosci', 'sortable' => false],
|
||||
['key' => 'transport', 'label' => 'Rodzaj transportu', 'sortable' => false],
|
||||
['key' => 'message', 'label' => 'Wiadomosc', 'sortable' => false],
|
||||
],
|
||||
$rows,
|
||||
[],
|
||||
[],
|
||||
[
|
||||
'page' => 1,
|
||||
'per_page' => max(1, count($rows)),
|
||||
'total' => count($rows),
|
||||
'total_pages' => 1,
|
||||
],
|
||||
[],
|
||||
[count($rows) > 0 ? count($rows) : 1],
|
||||
[],
|
||||
'/admin/shop_clients/details/?' . http_build_query([
|
||||
'name' => $name,
|
||||
'surname' => $surname,
|
||||
'email' => $email,
|
||||
]),
|
||||
'Brak zamowien klienta.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-clients/clients-details', [
|
||||
'name' => $name,
|
||||
'surname' => $surname,
|
||||
'email' => $email,
|
||||
'total_spent' => $totals['total_spent'],
|
||||
'ordersTable' => $ordersTable,
|
||||
'total_orders' => $totals['total_orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function clients_details(): string
|
||||
{
|
||||
return $this->details();
|
||||
}
|
||||
}
|
||||
353
autoload/admin/Controllers/ShopCouponController.php
Normal file
353
autoload/admin/Controllers/ShopCouponController.php
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Coupon\CouponRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopCouponController
|
||||
{
|
||||
private CouponRepository $repository;
|
||||
|
||||
public function __construct(CouponRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'status', 'used_count', 'name', 'type', 'amount', 'one_time', 'send', 'used', 'date_used'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'used',
|
||||
'label' => 'Uzyty',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- uzyty -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'send',
|
||||
'label' => 'Wyslany',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- wyslany -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$used = (int)($item['used'] ?? 0);
|
||||
$send = (int)($item['send'] ?? 0);
|
||||
$oneTime = (int)($item['one_time'] ?? 0);
|
||||
$type = (int)($item['type'] ?? 0);
|
||||
$amount = (string)($item['amount'] ?? '');
|
||||
$dateUsed = trim((string)($item['date_used'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'used_count' => (int)($item['used_count'] ?? 0),
|
||||
'name' => '<a href="/admin/shop_coupon/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'type' => htmlspecialchars((string)($type === 1 ? 'Rabat procentowy na koszyk' : '-'), ENT_QUOTES, 'UTF-8'),
|
||||
'amount' => $type === 1 && $amount !== '' ? htmlspecialchars($amount, ENT_QUOTES, 'UTF-8') . '%' : '-',
|
||||
'one_time' => $oneTime === 1 ? 'tak' : 'nie',
|
||||
'send' => $send === 1 ? 'tak' : 'nie',
|
||||
'used' => $used === 1 ? 'tak' : 'nie',
|
||||
'date_used' => $dateUsed !== '' ? htmlspecialchars($dateUsed, ENT_QUOTES, 'UTF-8') : '-',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_coupon/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/shop_coupon/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany kupon?',
|
||||
'confirm_ok' => 'Usun',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'used_count', 'sort_key' => 'used_count', 'label' => 'Uzyto X razy', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'type', 'sort_key' => 'type', 'label' => 'Typ kuponu', 'sortable' => true],
|
||||
['key' => 'amount', 'sort_key' => 'amount', 'label' => 'Wartosc', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'one_time', 'sort_key' => 'one_time', 'label' => 'Kupon jednorazowy', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'send', 'sort_key' => 'send', 'label' => 'Wyslany', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'used', 'sort_key' => 'used', 'label' => 'Uzyty', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'date_used', 'sort_key' => 'date_used', 'label' => 'Data uzycia', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_coupon/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/shop_coupon/edit/',
|
||||
'Dodaj kupon'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-coupon/coupons-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$coupon = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$categories = $this->repository->categoriesTree(null);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-coupon/coupon-edit-new', [
|
||||
'form' => $this->buildFormViewModel($coupon, $categories),
|
||||
]);
|
||||
}
|
||||
|
||||
public function coupon_edit(): string
|
||||
{
|
||||
return $this->edit();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania kuponu wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
if (is_array($values)) {
|
||||
$id = $this->repository->save($values);
|
||||
if (!empty($id)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Kupon zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
if (empty($payload['id'])) {
|
||||
$routeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($routeId > 0) {
|
||||
$payload['id'] = $routeId;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $this->repository->save($payload);
|
||||
if (!empty($id)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Kupon zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania kuponu wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function coupon_save(): void
|
||||
{
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Kupon zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_coupon/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function coupon_delete(): void
|
||||
{
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $coupon, array $categories): FormEditViewModel
|
||||
{
|
||||
$id = (int)($coupon['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => (string)($coupon['name'] ?? ''),
|
||||
'send' => (int)($coupon['send'] ?? 0),
|
||||
'status' => (int)($coupon['status'] ?? 1),
|
||||
'used' => (int)($coupon['used'] ?? 0),
|
||||
'type' => (int)($coupon['type'] ?? 1),
|
||||
'amount' => (string)($coupon['amount'] ?? ''),
|
||||
'one_time' => (int)($coupon['one_time'] ?? 1),
|
||||
'include_discounted_product' => (int)($coupon['include_discounted_product'] ?? 0),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'settings',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('send', [
|
||||
'label' => 'Wyslany',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::switch('used', [
|
||||
'label' => 'Uzyty',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::select('type', [
|
||||
'label' => 'Typ kuponu',
|
||||
'tab' => 'settings',
|
||||
'options' => [
|
||||
1 => 'Rabat procentowy na koszyk',
|
||||
],
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::text('amount', [
|
||||
'label' => 'Wartosc',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['class' => 'number-format'],
|
||||
]),
|
||||
FormField::switch('one_time', [
|
||||
'label' => 'Kupon jednorazowy',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::switch('include_discounted_product', [
|
||||
'label' => 'Dotyczy rowniez produktow przecenionych',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::custom('coupon_categories', \Shared\Tpl\Tpl::view('shop-coupon/coupon-categories-selector', [
|
||||
'label' => 'Ogranicz promocje do wybranych kategorii',
|
||||
'inputName' => 'categories[]',
|
||||
'categories' => $categories,
|
||||
'selectedIds' => is_array($coupon['categories'] ?? null) ? $coupon['categories'] : [],
|
||||
]), [
|
||||
'tab' => 'categories',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('categories', 'Kategorie', 'fa-folder-open'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_coupon/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_coupon/list/'),
|
||||
FormAction::cancel('/admin/shop_coupon/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-coupon-edit',
|
||||
$isNew ? 'Nowy kupon' : ('Edycja kuponu: ' . (string)($coupon['name'] ?? '')),
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_coupon/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
}
|
||||
364
autoload/admin/Controllers/ShopOrderController.php
Normal file
364
autoload/admin/Controllers/ShopOrderController.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Order\OrderAdminService;
|
||||
use Domain\Product\ProductRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
|
||||
class ShopOrderController
|
||||
{
|
||||
private OrderAdminService $service;
|
||||
private $productRepo;
|
||||
|
||||
public function __construct(OrderAdminService $service, ProductRepository $productRepo = null)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->productRepo = $productRepo;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return $this->view_list();
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
$sortableColumns = [
|
||||
'number',
|
||||
'date_order',
|
||||
'status',
|
||||
'summary',
|
||||
'client',
|
||||
'order_email',
|
||||
'client_phone',
|
||||
'transport',
|
||||
'payment_method',
|
||||
'total_orders',
|
||||
'paid',
|
||||
];
|
||||
|
||||
$statusOptions = ['' => '- status -'];
|
||||
foreach ($this->service->statuses() as $statusId => $statusName) {
|
||||
$statusOptions[(string)$statusId] = (string)$statusName;
|
||||
}
|
||||
|
||||
$filterDefinitions = [
|
||||
['key' => 'number', 'label' => 'Nr zamówienia', 'type' => 'text'],
|
||||
['key' => 'date_from', 'label' => 'Data od', 'type' => 'date'],
|
||||
['key' => 'date_to', 'label' => 'Data do', 'type' => 'date'],
|
||||
['key' => 'status', 'label' => 'Status', 'type' => 'select', 'options' => $statusOptions],
|
||||
['key' => 'client', 'label' => 'Klient', 'type' => 'text'],
|
||||
['key' => 'address', 'label' => 'Adres', 'type' => 'text'],
|
||||
['key' => 'order_email', 'label' => 'Email', 'type' => 'text'],
|
||||
['key' => 'client_phone', 'label' => 'Telefon', 'type' => 'text'],
|
||||
['key' => 'transport', 'label' => 'Dostawa', 'type' => 'text'],
|
||||
['key' => 'payment_method', 'label' => 'Płatność', 'type' => 'text'],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'date_order'
|
||||
);
|
||||
|
||||
$result = $this->service->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$listRequest['sortDir'],
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$statusesMap = $this->service->statuses();
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
|
||||
foreach ($result['items'] as $item) {
|
||||
$orderId = (int)($item['id'] ?? 0);
|
||||
$orderNumber = (string)($item['number'] ?? '');
|
||||
$statusId = (int)($item['status'] ?? 0);
|
||||
$statusLabel = (string)($statusesMap[$statusId] ?? ('Status #' . $statusId));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'date_order' => $this->formatDateTime((string)($item['date_order'] ?? '')),
|
||||
'number' => '<a href="/admin/shop_order/order_details/order_id=' . $orderId . '">' . htmlspecialchars($orderNumber, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'paid' => ((int)($item['paid'] ?? 0) === 1)
|
||||
? '<i class="fa fa-check text-success"></i>'
|
||||
: '<i class="fa fa-times text-dark"></i>',
|
||||
'status' => htmlspecialchars($statusLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'summary' => number_format((float)($item['summary'] ?? 0), 2, '.', ' ') . ' zł',
|
||||
'client' => htmlspecialchars((string)($item['client'] ?? ''), ENT_QUOTES, 'UTF-8') . ' | zamówienia: <strong>' . (int)($item['total_orders'] ?? 0) . '</strong>',
|
||||
'address' => (string)($item['address'] ?? ''),
|
||||
'order_email' => (string)($item['order_email'] ?? ''),
|
||||
'client_phone' => (string)($item['client_phone'] ?? ''),
|
||||
'transport' => (string)($item['transport'] ?? ''),
|
||||
'payment_method' => (string)($item['payment_method'] ?? ''),
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Szczegóły',
|
||||
'url' => '/admin/shop_order/order_details/order_id=' . $orderId,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usuń',
|
||||
'url' => '/admin/shop_order/order_delete/id=' . $orderId,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunąć wybrane zamówienie?',
|
||||
'confirm_ok' => 'Usuń',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'date_order', 'sort_key' => 'date_order', 'label' => 'Data dodania', 'class' => 'text-center', 'sortable' => true],
|
||||
['key' => 'number', 'sort_key' => 'number', 'label' => 'Nr zamówienia', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'paid', 'sort_key' => 'paid', 'label' => '', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Status', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'summary', 'sort_key' => 'summary', 'label' => 'Wartość', 'class' => 'text-right align-middle', 'sortable' => true],
|
||||
['key' => 'client', 'sort_key' => 'client', 'label' => 'Klient', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'address', 'label' => 'Adres', 'sortable' => false],
|
||||
['key' => 'order_email', 'sort_key' => 'order_email', 'label' => 'Email', 'sortable' => true],
|
||||
['key' => 'client_phone', 'sort_key' => 'client_phone', 'label' => 'Telefon', 'sortable' => true],
|
||||
['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true],
|
||||
['key' => 'payment_method', 'sort_key' => 'payment_method', 'label' => 'Płatność', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $listRequest['sortDir'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_order/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-order/orders-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function details(): string
|
||||
{
|
||||
return $this->order_details();
|
||||
}
|
||||
|
||||
public function order_details(): string
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
$order = $this->service->details($orderId);
|
||||
|
||||
$coupon = null;
|
||||
if (!empty($order) && !empty($order['coupon_id'])) {
|
||||
$coupon = ( new \Domain\Coupon\CouponRepository( $GLOBALS['mdb'] ) )->find((int)$order['coupon_id']);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-order/order-details', [
|
||||
'order' => $order,
|
||||
'coupon' => $coupon,
|
||||
'order_statuses' => $this->service->statuses(),
|
||||
'next_order_id' => $this->service->nextOrderId($orderId),
|
||||
'prev_order_id' => $this->service->prevOrderId($orderId),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
return $this->order_edit();
|
||||
}
|
||||
|
||||
public function order_edit(): string
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
$transports = ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->allActive();
|
||||
|
||||
// Dane transportów do JS (id, cost, delivery_free)
|
||||
$transportsJson = [];
|
||||
if (is_array($transports)) {
|
||||
foreach ($transports as $t) {
|
||||
$transportsJson[] = [
|
||||
'id' => (int)$t['id'],
|
||||
'cost' => (float)$t['cost'],
|
||||
'delivery_free' => (int)($t['delivery_free'] ?? 0),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-order/order-edit', [
|
||||
'order' => $this->service->details($orderId),
|
||||
'order_statuses' => $this->service->statuses(),
|
||||
'transport' => $transports,
|
||||
'payment_methods' => ( new \Domain\PaymentMethod\PaymentMethodRepository( $GLOBALS['mdb'] ) )->allActive(),
|
||||
'free_delivery' => $this->service->getFreeDeliveryThreshold(),
|
||||
'transports_json' => json_encode($transportsJson),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->order_save();
|
||||
}
|
||||
|
||||
public function order_save(): void
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
|
||||
// Zapisz produkty PRZED zapisem zamówienia (bo saveOrderByAdmin przelicza summary)
|
||||
$productsData = \Shared\Helpers\Helpers::get('products');
|
||||
if (is_array($productsData)) {
|
||||
$this->service->saveOrderProducts($orderId, $productsData);
|
||||
}
|
||||
|
||||
$saved = $this->service->saveOrderByAdmin([
|
||||
'order_id' => $orderId,
|
||||
'client_name' => (string)\Shared\Helpers\Helpers::get('client_name'),
|
||||
'client_surname' => (string)\Shared\Helpers\Helpers::get('client_surname'),
|
||||
'client_street' => (string)\Shared\Helpers\Helpers::get('client_street'),
|
||||
'client_postal_code' => (string)\Shared\Helpers\Helpers::get('client_postal_code'),
|
||||
'client_city' => (string)\Shared\Helpers\Helpers::get('client_city'),
|
||||
'client_email' => (string)\Shared\Helpers\Helpers::get('client_email'),
|
||||
'firm_name' => (string)\Shared\Helpers\Helpers::get('firm_name'),
|
||||
'firm_street' => (string)\Shared\Helpers\Helpers::get('firm_street'),
|
||||
'firm_postal_code' => (string)\Shared\Helpers\Helpers::get('firm_postal_code'),
|
||||
'firm_city' => (string)\Shared\Helpers\Helpers::get('firm_city'),
|
||||
'firm_nip' => (string)\Shared\Helpers\Helpers::get('firm_nip'),
|
||||
'transport_id' => (int)\Shared\Helpers\Helpers::get('transport_id'),
|
||||
'inpost_paczkomat' => (string)\Shared\Helpers\Helpers::get('inpost_paczkomat'),
|
||||
'payment_method_id' => (int)\Shared\Helpers\Helpers::get('payment_method_id'),
|
||||
]);
|
||||
|
||||
if ($saved) {
|
||||
\Shared\Helpers\Helpers::alert('Zamówienie zostało zapisane.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_order/order_details/order_id=' . $orderId);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function search_products_ajax(): void
|
||||
{
|
||||
$query = trim((string)\Shared\Helpers\Helpers::get('query'));
|
||||
$langId = trim((string)\Shared\Helpers\Helpers::get('lang_id'));
|
||||
if ($langId === '') {
|
||||
$langId = isset($_SESSION['lang_id']) ? (string)$_SESSION['lang_id'] : 'pl';
|
||||
}
|
||||
|
||||
$results = $this->service->searchProducts($query, $langId);
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['status' => 'ok', 'products' => $results]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function notes_save(): void
|
||||
{
|
||||
$this->service->saveNotes((int)\Shared\Helpers\Helpers::get('order_id'), (string)\Shared\Helpers\Helpers::get('notes'));
|
||||
}
|
||||
|
||||
public function order_status_change(): void
|
||||
{
|
||||
$response = $this->service->changeStatus(
|
||||
(int)\Shared\Helpers\Helpers::get('order_id'),
|
||||
(int)\Shared\Helpers\Helpers::get('status'),
|
||||
(string)\Shared\Helpers\Helpers::get('email') === 'true'
|
||||
);
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function order_resend_confirmation_email(): void
|
||||
{
|
||||
$response = $this->service->resendConfirmationEmail((int)\Shared\Helpers\Helpers::get('order_id'));
|
||||
|
||||
echo json_encode(['result' => $response]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function set_order_as_unpaid(): void
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
$this->service->setOrderAsUnpaid($orderId);
|
||||
|
||||
header('Location: /admin/shop_order/order_details/order_id=' . $orderId);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function set_order_as_paid(): void
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
$this->service->setOrderAsPaid($orderId, (int)\Shared\Helpers\Helpers::get('send_mail') === 1);
|
||||
|
||||
header('Location: /admin/shop_order/order_details/order_id=' . $orderId);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function send_order_to_apilo(): void
|
||||
{
|
||||
$orderId = (int)\Shared\Helpers\Helpers::get('order_id');
|
||||
|
||||
if ($this->service->sendOrderToApilo($orderId)) {
|
||||
\Shared\Helpers\Helpers::alert('Zamówienie zostanie wysłane ponownie do apilo.com');
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::alert('Wystąpił błąd podczas wysyłania zamówienia do apilo.com');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_order/order_details/order_id=' . $orderId);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function toggle_trustmate_send(): void
|
||||
{
|
||||
echo json_encode($this->service->toggleTrustmateSend((int)\Shared\Helpers\Helpers::get('order_id')));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
$this->order_delete();
|
||||
}
|
||||
|
||||
public function order_delete(): void
|
||||
{
|
||||
if ($this->service->deleteOrder((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Zamówienie zostało usunięte');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_order/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
private function formatDateTime(string $value): string
|
||||
{
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$ts = strtotime($value);
|
||||
if ($ts === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return date('Y-m-d H:i', $ts);
|
||||
}
|
||||
}
|
||||
294
autoload/admin/Controllers/ShopPaymentMethodController.php
Normal file
294
autoload/admin/Controllers/ShopPaymentMethodController.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\PaymentMethod\PaymentMethodRepository;
|
||||
use Domain\Integrations\IntegrationsRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopPaymentMethodController
|
||||
{
|
||||
private PaymentMethodRepository $repository;
|
||||
|
||||
public function __construct(PaymentMethodRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'status', 'apilo_payment_type_id'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'id'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$apiloPaymentTypes = $this->getApiloPaymentTypes();
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$apiloPaymentTypeId = $item['apilo_payment_type_id'] ?? null;
|
||||
|
||||
$apiloLabel = '-';
|
||||
if ($apiloPaymentTypeId !== null) {
|
||||
$apiloKey = (string)$apiloPaymentTypeId;
|
||||
if (isset($apiloPaymentTypes[$apiloKey])) {
|
||||
$apiloLabel = $apiloPaymentTypes[$apiloKey];
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'name' => '<a href="/admin/shop_payment_method/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'apilo_payment_type' => htmlspecialchars((string)$apiloLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_payment_method/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'apilo_payment_type', 'sort_key' => 'apilo_payment_type_id', 'label' => 'Typ platnosci Apilo', 'class' => 'text-center', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_payment_method/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-payment-method/payment-methods-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$paymentMethod = $this->repository->find((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if ($paymentMethod === null) {
|
||||
\Shared\Helpers\Helpers::alert('Metoda platnosci nie zostala znaleziona.');
|
||||
header('Location: /admin/shop_payment_method/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-payment-method/payment-method-edit', [
|
||||
'form' => $this->buildFormViewModel($paymentMethod, $this->getApiloPaymentTypes()),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$payload = $_POST;
|
||||
$paymentMethodId = isset($payload['id']) && $payload['id'] !== ''
|
||||
? (int)$payload['id']
|
||||
: (int)\Shared\Helpers\Helpers::get('id');
|
||||
|
||||
$id = $this->repository->save($paymentMethodId, $payload);
|
||||
if ($id !== null) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Metoda platnosci zostala zapisana.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania metody platnosci wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $paymentMethod, array $apiloPaymentTypes): FormEditViewModel
|
||||
{
|
||||
$id = (int)($paymentMethod['id'] ?? 0);
|
||||
$name = (string)($paymentMethod['name'] ?? '');
|
||||
|
||||
$apiloOptions = ['' => '--- wybierz typ platnosci apilo.com ---'];
|
||||
foreach ($apiloPaymentTypes as $apiloId => $apiloName) {
|
||||
$apiloOptions[(string)$apiloId] = $apiloName;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'description' => (string)($paymentMethod['description'] ?? ''),
|
||||
'status' => (int)($paymentMethod['status'] ?? 0),
|
||||
'apilo_payment_type_id' => $paymentMethod['apilo_payment_type_id'] ?? '',
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::custom(
|
||||
'name_preview',
|
||||
\Shared\Html\Html::input([
|
||||
'label' => 'Nazwa',
|
||||
'name' => 'name_preview',
|
||||
'id' => 'name_preview',
|
||||
'value' => $name,
|
||||
'type' => 'text',
|
||||
'readonly' => true,
|
||||
]),
|
||||
['tab' => 'settings']
|
||||
),
|
||||
FormField::textarea('description', [
|
||||
'label' => 'Opis',
|
||||
'tab' => 'settings',
|
||||
'rows' => 5,
|
||||
]),
|
||||
FormField::select('apilo_payment_type_id', [
|
||||
'label' => 'Typ platnosci Apilo',
|
||||
'tab' => 'settings',
|
||||
'options' => $apiloOptions,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_payment_method/save/id=' . $id;
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_payment_method/list/'),
|
||||
FormAction::cancel('/admin/shop_payment_method/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-payment-method-edit',
|
||||
'Edycja metody platnosci: ' . $name,
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_payment_method/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
private function getApiloPaymentTypes(): array
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$integrationsRepository = new IntegrationsRepository( $mdb );
|
||||
$rawSetting = $integrationsRepository -> getSetting( 'apilo', 'payment-types-list' );
|
||||
$raw = null;
|
||||
|
||||
if (is_array($rawSetting)) {
|
||||
$raw = $rawSetting;
|
||||
} elseif (is_string($rawSetting)) {
|
||||
$decoded = @unserialize($rawSetting);
|
||||
if (is_array($decoded)) {
|
||||
$raw = $decoded;
|
||||
} else {
|
||||
$decodedJson = json_decode($rawSetting, true);
|
||||
if (is_array($decodedJson)) {
|
||||
$raw = $decodedJson;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isset($raw['message']) && isset($raw['code'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isset($raw['items']) && is_array($raw['items'])) {
|
||||
$raw = $raw['items'];
|
||||
} elseif (isset($raw['data']) && is_array($raw['data'])) {
|
||||
$raw = $raw['data'];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($raw as $key => $paymentType) {
|
||||
if (is_array($paymentType)) {
|
||||
if (isset($paymentType['id'], $paymentType['name'])) {
|
||||
$list[(string)$paymentType['id']] = (string)$paymentType['name'];
|
||||
continue;
|
||||
}
|
||||
} elseif (is_scalar($paymentType)) {
|
||||
if (is_int($key) || (is_string($key) && preg_match('/^-?\d+$/', $key) === 1)) {
|
||||
$list[(string)$key] = (string)$paymentType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
379
autoload/admin/Controllers/ShopProducerController.php
Normal file
379
autoload/admin/Controllers/ShopProducerController.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Producer\ProducerRepository;
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class ShopProducerController
|
||||
{
|
||||
private ProducerRepository $repository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(ProducerRepository $repository, LanguagesRepository $languagesRepository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->languagesRepository = $languagesRepository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'status'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$img = trim((string)($item['img'] ?? ''));
|
||||
|
||||
$imgHtml = '';
|
||||
if ($img !== '') {
|
||||
$imgHtml = '<img src="' . htmlspecialchars($img, ENT_QUOTES, 'UTF-8') . '" style="max-height:30px;" />';
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'name' => '<a href="/admin/shop_producer/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'img' => $imgHtml,
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_producer/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/shop_producer/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybranego producenta?',
|
||||
'confirm_ok' => 'Usun',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'img', 'label' => 'Logo', 'sortable' => false, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_producer/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/shop_producer/edit/',
|
||||
'Dodaj producenta'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-producer/producers-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$producer = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$validationErrors = $_SESSION['form_errors'][$this->formId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->formId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-producer/producer-edit', [
|
||||
'form' => $this->buildFormViewModel($producer, $languages, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function producer_edit(): string
|
||||
{
|
||||
return $this->edit();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
// Legacy JSON (gridEdit)
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania producenta wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
if (is_array($values)) {
|
||||
$langs = $this->languagesRepository->languagesList(true);
|
||||
|
||||
$id = $this->repository->save(
|
||||
(int)($values['id'] ?? 0),
|
||||
(string)($values['name'] ?? ''),
|
||||
$this->toSwitchValue($values['status'] ?? 0),
|
||||
$values['img'] ?? null,
|
||||
$values['description'] ?? [],
|
||||
$values['data'] ?? [],
|
||||
$values['meta_title'] ?? [],
|
||||
$langs
|
||||
);
|
||||
|
||||
if (!empty($id)) {
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Producent zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Nowy flow (form-edit)
|
||||
$producer = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$languages = $this->languagesRepository->languagesList();
|
||||
$form = $this->buildFormViewModel($producer, $languages);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($form, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->formId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$langs = $this->languagesRepository->languagesList(true);
|
||||
|
||||
$translations = $data['translations'] ?? [];
|
||||
$description = [];
|
||||
$metaData = [];
|
||||
$metaTitle = [];
|
||||
foreach ($translations as $langId => $fields) {
|
||||
$description[$langId] = $fields['description'] ?? null;
|
||||
$metaData[$langId] = $fields['data'] ?? null;
|
||||
$metaTitle[$langId] = $fields['meta_title'] ?? null;
|
||||
}
|
||||
|
||||
$savedId = $this->repository->save(
|
||||
(int)($data['id'] ?? 0),
|
||||
(string)($data['name'] ?? ''),
|
||||
$this->toSwitchValue($data['status'] ?? 0),
|
||||
$data['img'] ?? null,
|
||||
$description,
|
||||
$metaData,
|
||||
$metaTitle,
|
||||
$langs
|
||||
);
|
||||
|
||||
if ($savedId) {
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => $savedId,
|
||||
'message' => 'Producent zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania producenta wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function producer_save(): void
|
||||
{
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::htacces();
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
\Shared\Helpers\Helpers::alert('Producent zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_producer/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function producer_delete(): void
|
||||
{
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $producer, array $languages, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$id = (int)($producer['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => (string)($producer['name'] ?? ''),
|
||||
'status' => (int)($producer['status'] ?? 1),
|
||||
'img' => $producer['img'] ?? null,
|
||||
'languages' => is_array($producer['languages'] ?? null) ? $producer['languages'] : [],
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'required' => true,
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'general',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::image('img', [
|
||||
'label' => 'Logo',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::langSection('translations', 'description', [
|
||||
FormField::editor('description', [
|
||||
'label' => 'Opis',
|
||||
'height' => 250,
|
||||
]),
|
||||
FormField::editor('data', [
|
||||
'label' => 'Dane producenta',
|
||||
'height' => 250,
|
||||
]),
|
||||
]),
|
||||
FormField::langSection('translations', 'seo', [
|
||||
FormField::text('meta_title', [
|
||||
'label' => 'Meta title',
|
||||
]),
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('general', 'Ogolne', 'fa-file'),
|
||||
new FormTab('description', 'Opis', 'fa-file'),
|
||||
new FormTab('seo', 'SEO', 'fa-globe'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_producer/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_producer/list/'),
|
||||
FormAction::cancel('/admin/shop_producer/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->formId(),
|
||||
'Edycja producenta',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_producer/list/',
|
||||
true,
|
||||
[],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function formId(): string
|
||||
{
|
||||
return 'shop-producer-edit';
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1193
autoload/admin/Controllers/ShopProductController.php
Normal file
1193
autoload/admin/Controllers/ShopProductController.php
Normal file
File diff suppressed because it is too large
Load Diff
328
autoload/admin/Controllers/ShopProductSetsController.php
Normal file
328
autoload/admin/Controllers/ShopProductSetsController.php
Normal file
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\ProductSet\ProductSetRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopProductSetsController
|
||||
{
|
||||
private ProductSetRepository $repository;
|
||||
|
||||
public function __construct(ProductSetRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'status'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'name' => '<a href="/admin/shop_product_sets/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_product_sets/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/shop_product_sets/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrany komplet produktow?',
|
||||
'confirm_ok' => 'Usun',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_product_sets/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/shop_product_sets/edit/',
|
||||
'Dodaj komplet produktow'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-product-sets/product-sets-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
return $this->list();
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$set = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$products = $this->repository->allProductsMap();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-product-sets/product-set-edit', [
|
||||
'form' => $this->buildFormViewModel($set, $products),
|
||||
]);
|
||||
}
|
||||
|
||||
public function set_edit(): string
|
||||
{
|
||||
return $this->edit();
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania kompletu produktow wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
if (is_array($values)) {
|
||||
$productIds = $values['set_products_id'] ?? [];
|
||||
if (!is_array($productIds)) {
|
||||
$productIds = $productIds ? [$productIds] : [];
|
||||
}
|
||||
|
||||
$id = $this->repository->save(
|
||||
(int)($values['id'] ?? 0),
|
||||
(string)($values['name'] ?? ''),
|
||||
$this->toSwitchValue($values['status'] ?? 0),
|
||||
$productIds
|
||||
);
|
||||
|
||||
if (!empty($id)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Komplet produktow zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
if (empty($payload['id'])) {
|
||||
$routeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($routeId > 0) {
|
||||
$payload['id'] = $routeId;
|
||||
}
|
||||
}
|
||||
|
||||
$productIds = $payload['set_products_id'] ?? [];
|
||||
if (!is_array($productIds)) {
|
||||
$productIds = $productIds ? [$productIds] : [];
|
||||
}
|
||||
|
||||
$id = $this->repository->save(
|
||||
(int)($payload['id'] ?? 0),
|
||||
(string)($payload['name'] ?? ''),
|
||||
$this->toSwitchValue($payload['status'] ?? 0),
|
||||
$productIds
|
||||
);
|
||||
|
||||
if (!empty($id)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Komplet produktow zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania kompletu produktow wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Komplet produktow zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_product_sets/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function set_delete(): void
|
||||
{
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $set, array $products = []): FormEditViewModel
|
||||
{
|
||||
$id = (int)($set['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
$selectedProducts = $set['products'] ?? [];
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => (string)($set['name'] ?? ''),
|
||||
'status' => (int)($set['status'] ?? 1),
|
||||
];
|
||||
|
||||
$productsSelectHtml = $this->renderProductsSelect($products, $selectedProducts);
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'settings',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::custom('set_products', $productsSelectHtml, [
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_product_sets/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_product_sets/list/'),
|
||||
FormAction::cancel('/admin/shop_product_sets/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-product-set-edit',
|
||||
$isNew ? 'Nowy komplet produktow' : ('Edycja kompletu produktow: ' . (string)($set['name'] ?? '')),
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_product_sets/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if (is_numeric($value)) {
|
||||
return ((int)$value) === 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$normalized = strtolower(trim($value));
|
||||
return in_array($normalized, ['1', 'on', 'true', 'yes'], true) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function renderProductsSelect(array $products, array $selectedProducts): string
|
||||
{
|
||||
$html = '<div class="form-group row">';
|
||||
$html .= '<label class="col-lg-4 control-label">Produkty do kompletu:</label>';
|
||||
$html .= '<div class="col-lg-8">';
|
||||
$html .= '<select id="set_products_id" multiple name="set_products_id[]" placeholder="produkty do kompletu">';
|
||||
$html .= '<option value="">wybierz produkt...</option>';
|
||||
|
||||
foreach ($products as $productId => $productName) {
|
||||
$pid = (int)$productId;
|
||||
$selected = in_array($pid, $selectedProducts, true) ? ' selected' : '';
|
||||
$html .= '<option value="' . $pid . '"' . $selected . '>'
|
||||
. htmlspecialchars((string)$productName, ENT_QUOTES, 'UTF-8')
|
||||
. '</option>';
|
||||
}
|
||||
|
||||
$html .= '</select>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
327
autoload/admin/Controllers/ShopPromotionController.php
Normal file
327
autoload/admin/Controllers/ShopPromotionController.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Promotion\PromotionRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopPromotionController
|
||||
{
|
||||
private PromotionRepository $repository;
|
||||
|
||||
public function __construct(PromotionRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'status', 'condition_type', 'date_from', 'date_to'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'id'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'DESC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$conditionType = (int)($item['condition_type'] ?? 0);
|
||||
$dateFrom = trim((string)($item['date_from'] ?? ''));
|
||||
$dateTo = trim((string)($item['date_to'] ?? ''));
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'name' => '<a href="/admin/shop_promotion/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'condition_type' => htmlspecialchars((string)(\Domain\Promotion\PromotionRepository::$condition_type[$conditionType] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
||||
'date_from' => $dateFrom !== '' ? htmlspecialchars($dateFrom, ENT_QUOTES, 'UTF-8') : '-',
|
||||
'date_to' => $dateTo !== '' ? htmlspecialchars($dateTo, ENT_QUOTES, 'UTF-8') : '-',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_promotion/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/shop_promotion/delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybrana promocje?',
|
||||
'confirm_ok' => 'Usun',
|
||||
'confirm_cancel' => 'Anuluj',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'condition_type', 'sort_key' => 'condition_type', 'label' => 'Typ kuponu', 'sortable' => true],
|
||||
['key' => 'date_from', 'sort_key' => 'date_from', 'label' => 'Data od', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'date_to', 'sort_key' => 'date_to', 'label' => 'Data do', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_promotion/list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/shop_promotion/edit/',
|
||||
'Dodaj promocje'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-promotion/promotions-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$promotion = $this->repository->find((int)\Shared\Helpers\Helpers::get('id')) ?: [];
|
||||
$categories = $this->repository->categoriesTree(null);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-promotion/promotion-edit', [
|
||||
'form' => $this->buildFormViewModel($promotion, $categories),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania promocji wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
if (is_array($values)) {
|
||||
$id = $this->repository->save($values);
|
||||
if (!empty($id)) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Promocja zostala zapisana.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
if (empty($payload['id'])) {
|
||||
$routeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($routeId > 0) {
|
||||
$payload['id'] = $routeId;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $this->repository->save($payload);
|
||||
if (!empty($id)) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Promocja zostala zapisana.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania promocji wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Promocja zostala usunieta.');
|
||||
}
|
||||
|
||||
header('Location: /admin/shop_promotion/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $promotion, array $categories): FormEditViewModel
|
||||
{
|
||||
$id = (int)($promotion['id'] ?? 0);
|
||||
$isNew = $id <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => (string)($promotion['name'] ?? ''),
|
||||
'status' => (int)($promotion['status'] ?? 1),
|
||||
'include_coupon' => (int)($promotion['include_coupon'] ?? 0),
|
||||
'include_product_promo' => (int)($promotion['include_product_promo'] ?? 0),
|
||||
'condition_type' => (int)($promotion['condition_type'] ?? 1),
|
||||
'discount_type' => (int)($promotion['discount_type'] ?? 1),
|
||||
'min_product_count' => $promotion['min_product_count'] ?? '',
|
||||
'price_cheapest_product' => $promotion['price_cheapest_product'] ?? '',
|
||||
'amount' => $promotion['amount'] ?? '',
|
||||
'date_from' => (string)($promotion['date_from'] ?? ''),
|
||||
'date_to' => (string)($promotion['date_to'] ?? ''),
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'settings',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywna',
|
||||
'tab' => 'settings',
|
||||
'value' => true,
|
||||
]),
|
||||
FormField::switch('include_coupon', [
|
||||
'label' => 'Lacz z kuponami rabatowymi',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::switch('include_product_promo', [
|
||||
'label' => 'Uwzglednij produkty przecenione',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::select('condition_type', [
|
||||
'label' => 'Warunki promocji',
|
||||
'tab' => 'settings',
|
||||
'options' => \Domain\Promotion\PromotionRepository::$condition_type,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::select('discount_type', [
|
||||
'label' => 'Typ rabatu',
|
||||
'tab' => 'settings',
|
||||
'options' => \Domain\Promotion\PromotionRepository::$discount_type,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::text('min_product_count', [
|
||||
'label' => 'Min. ilosc produktow z danej kategorii',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['class' => 'int-format'],
|
||||
]),
|
||||
FormField::text('price_cheapest_product', [
|
||||
'label' => 'Cena najtanszego produktu',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['class' => 'number-format'],
|
||||
]),
|
||||
FormField::text('amount', [
|
||||
'label' => 'Wartosc',
|
||||
'tab' => 'settings',
|
||||
'attributes' => ['class' => 'number-format'],
|
||||
]),
|
||||
FormField::date('date_from', [
|
||||
'label' => 'Data od',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::date('date_to', [
|
||||
'label' => 'Data do',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::custom('categories_group_1', \Shared\Tpl\Tpl::view('shop-promotion/promotion-categories-selector', [
|
||||
'label' => 'Kategorie grupa I',
|
||||
'inputName' => 'categories[]',
|
||||
'categories' => $categories,
|
||||
'selectedIds' => is_array($promotion['categories'] ?? null) ? $promotion['categories'] : [],
|
||||
]), [
|
||||
'tab' => 'categories',
|
||||
]),
|
||||
FormField::custom('categories_group_2', \Shared\Tpl\Tpl::view('shop-promotion/promotion-categories-selector', [
|
||||
'label' => 'Kategorie grupa II',
|
||||
'inputName' => 'condition_categories[]',
|
||||
'categories' => $categories,
|
||||
'selectedIds' => is_array($promotion['condition_categories'] ?? null) ? $promotion['condition_categories'] : [],
|
||||
]), [
|
||||
'tab' => 'categories',
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('categories', 'Kategorie', 'fa-folder-open'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_promotion/save/' . ($isNew ? '' : ('id=' . $id));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_promotion/list/'),
|
||||
FormAction::cancel('/admin/shop_promotion/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-promotion-edit',
|
||||
$isNew ? 'Nowa promocja' : ('Edycja promocji: ' . (string)($promotion['name'] ?? '')),
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_promotion/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
265
autoload/admin/Controllers/ShopStatusesController.php
Normal file
265
autoload/admin/Controllers/ShopStatusesController.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\ShopStatus\ShopStatusRepository;
|
||||
use Domain\Integrations\IntegrationsRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopStatusesController
|
||||
{
|
||||
private ShopStatusRepository $repository;
|
||||
|
||||
public function __construct(ShopStatusRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'status', 'color', 'o', 'apilo_status_id'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Status',
|
||||
'type' => 'text',
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'o'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$apiloStatusList = $this->getApiloStatusList();
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$statusName = trim((string)($item['status'] ?? ''));
|
||||
$color = trim((string)($item['color'] ?? ''));
|
||||
$apiloStatusId = $item['apilo_status_id'] ?? null;
|
||||
|
||||
$apiloStatusLabel = '';
|
||||
if ($apiloStatusId !== null && isset($apiloStatusList[$apiloStatusId])) {
|
||||
$apiloStatusLabel = $apiloStatusList[$apiloStatusId];
|
||||
}
|
||||
|
||||
$colorHtml = $color !== ''
|
||||
? '<span style="display:inline-block;width:20px;height:20px;background:' . htmlspecialchars($color, ENT_QUOTES, 'UTF-8') . ';border:1px solid #ccc;vertical-align:middle;margin-right:5px;"></span> ' . htmlspecialchars($color, ENT_QUOTES, 'UTF-8')
|
||||
: '-';
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'status' => '<a href="/admin/shop_statuses/edit/id=' . $id . '">' . htmlspecialchars($statusName, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'color' => $colorHtml,
|
||||
'apilo_status' => htmlspecialchars($apiloStatusLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_statuses/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Status', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'color', 'sort_key' => 'color', 'label' => 'Kolor', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'apilo_status', 'sort_key' => 'apilo_status_id', 'label' => 'Status Apilo', 'sortable' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_statuses/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-statuses/view-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$status = $this->repository->find((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if ($status === null) {
|
||||
\Shared\Helpers\Helpers::alert('Status nie zostal znaleziony.');
|
||||
header('Location: /admin/shop_statuses/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$apiloStatusList = $this->getApiloStatusList();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-statuses/status-edit', [
|
||||
'form' => $this->buildFormViewModel($status, $apiloStatusList),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'msg' => 'Podczas zapisywania statusu wystapil blad. Prosze sprobowac ponownie.',
|
||||
];
|
||||
|
||||
if (is_array($values)) {
|
||||
$statusId = (int)($values['id'] ?? 0);
|
||||
$id = $this->repository->save($statusId, $values);
|
||||
if ($id !== null && $id >= 0) {
|
||||
$response = [
|
||||
'status' => 'ok',
|
||||
'msg' => 'Status zostal zapisany.',
|
||||
'id' => (int)$id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = $_POST;
|
||||
$statusId = isset($payload['id']) && $payload['id'] !== '' ? (int)$payload['id'] : null;
|
||||
if ($statusId === null) {
|
||||
$statusId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
}
|
||||
|
||||
$id = $this->repository->save($statusId, $payload);
|
||||
if ($id !== null && $id >= 0) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Status zostal zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania statusu wystapil blad.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $status, array $apiloStatusList): FormEditViewModel
|
||||
{
|
||||
$id = (int)($status['id'] ?? 0);
|
||||
|
||||
$apiloOptions = ['' => '--- wybierz status apilo.com ---'];
|
||||
foreach ($apiloStatusList as $apiloId => $apiloName) {
|
||||
$apiloOptions[$apiloId] = $apiloName;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'status' => (string)($status['status'] ?? ''),
|
||||
'color' => (string)($status['color'] ?? ''),
|
||||
'apilo_status_id' => $status['apilo_status_id'] ?? '',
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('status', [
|
||||
'label' => 'Status',
|
||||
'tab' => 'settings',
|
||||
'readonly' => true,
|
||||
]),
|
||||
FormField::color('color', [
|
||||
'label' => 'Kolor',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::select('apilo_status_id', [
|
||||
'label' => 'Status z Apilo',
|
||||
'tab' => 'settings',
|
||||
'options' => $apiloOptions,
|
||||
]),
|
||||
];
|
||||
|
||||
$tabs = [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_statuses/save/id=' . $id;
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_statuses/list/'),
|
||||
FormAction::cancel('/admin/shop_statuses/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'status-edit',
|
||||
'Edycja statusu zamowienia',
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_statuses/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
private function getApiloStatusList(): array
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$integrationsRepository = new IntegrationsRepository( $mdb );
|
||||
|
||||
$list = [];
|
||||
$raw = @unserialize( $integrationsRepository -> getSetting( 'apilo', 'status-types-list' ) );
|
||||
if (is_array($raw)) {
|
||||
foreach ($raw as $apiloStatus) {
|
||||
if (isset($apiloStatus['id'], $apiloStatus['name'])) {
|
||||
$list[(int)$apiloStatus['id']] = (string)$apiloStatus['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
346
autoload/admin/Controllers/ShopTransportController.php
Normal file
346
autoload/admin/Controllers/ShopTransportController.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Transport\TransportRepository;
|
||||
use Domain\PaymentMethod\PaymentMethodRepository;
|
||||
use Domain\Integrations\IntegrationsRepository;
|
||||
use admin\ViewModels\Common\PaginatedTableViewModel;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
|
||||
class ShopTransportController
|
||||
{
|
||||
private TransportRepository $transportRepository;
|
||||
private PaymentMethodRepository $paymentMethodRepository;
|
||||
|
||||
public function __construct(
|
||||
TransportRepository $transportRepository,
|
||||
PaymentMethodRepository $paymentMethodRepository
|
||||
) {
|
||||
$this->transportRepository = $transportRepository;
|
||||
$this->paymentMethodRepository = $paymentMethodRepository;
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
$sortableColumns = ['id', 'name', 'status', 'cost', 'max_wp', 'default', 'o'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'name',
|
||||
'label' => 'Nazwa',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'name'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->transportRepository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$apiloCarrierAccounts = $this->getApiloCarrierAccounts();
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)($item['id'] ?? 0);
|
||||
$name = trim((string)($item['name'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
$cost = (float)($item['cost'] ?? 0.0);
|
||||
$maxWp = $item['max_wp'] ?? null;
|
||||
$default = (int)($item['default'] ?? 0);
|
||||
$apiloCarrierAccountId = $item['apilo_carrier_account_id'] ?? null;
|
||||
|
||||
$apiloLabel = '-';
|
||||
if ($apiloCarrierAccountId !== null) {
|
||||
$apiloKey = (string)$apiloCarrierAccountId;
|
||||
if (isset($apiloCarrierAccounts[$apiloKey])) {
|
||||
$apiloLabel = $apiloCarrierAccounts[$apiloKey];
|
||||
}
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'default' => $default === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'cost' => \Shared\Helpers\Helpers::decimal($cost) . ' zł',
|
||||
'max_wp' => $maxWp !== null ? (int)$maxWp : '-',
|
||||
'name' => '<a href="/admin/shop_transport/edit/id=' . $id . '">' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'apilo_carrier' => htmlspecialchars((string)$apiloLabel, ENT_QUOTES, 'UTF-8'),
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/shop_transport/edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'default', 'sort_key' => 'default', 'label' => 'Domyślna FT', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'cost', 'sort_key' => 'cost', 'label' => 'Koszt', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'max_wp', 'sort_key' => 'max_wp', 'label' => 'Maks. WP', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'name', 'sort_key' => 'name', 'label' => 'Nazwa', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'apilo_carrier', 'label' => 'Typ kuriera Apilo', 'class' => 'text-center', 'sortable' => false],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/shop_transport/list/',
|
||||
'Brak danych w tabeli.'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-transport/transports-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(): string
|
||||
{
|
||||
$transport = $this->transportRepository->find((int)\Shared\Helpers\Helpers::get('id'));
|
||||
if ($transport === null) {
|
||||
\Shared\Helpers\Helpers::alert('Rodzaj transportu nie został znaleziony.');
|
||||
header('Location: /admin/shop_transport/list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
$paymentMethods = $this->paymentMethodRepository->allForAdmin();
|
||||
$apiloCarrierAccounts = $this->getApiloCarrierAccounts();
|
||||
|
||||
return \Shared\Tpl\Tpl::view('shop-transport/transport-edit', [
|
||||
'form' => $this->buildFormViewModel($transport, $paymentMethods, $apiloCarrierAccounts),
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$payload = $_POST;
|
||||
$transportId = isset($payload['id']) && $payload['id'] !== ''
|
||||
? (int)$payload['id']
|
||||
: (int)\Shared\Helpers\Helpers::get('id');
|
||||
|
||||
$payload['id'] = $transportId;
|
||||
|
||||
$id = $this->transportRepository->save($payload);
|
||||
if ($id !== null) {
|
||||
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id' => (int)$id,
|
||||
'message' => 'Rodzaj transportu został zapisany.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['general' => 'Podczas zapisywania rodzaju transportu wystąpił błąd.'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
private function buildFormViewModel(
|
||||
array $transport,
|
||||
array $paymentMethods,
|
||||
array $apiloCarrierAccounts
|
||||
): FormEditViewModel {
|
||||
$id = (int)($transport['id'] ?? 0);
|
||||
$name = (string)($transport['name'] ?? '');
|
||||
|
||||
$apiloOptions = ['' => '--- wybierz konto przewoźnika ---'];
|
||||
foreach ($apiloCarrierAccounts as $carrierId => $carrierName) {
|
||||
$apiloOptions[(string)$carrierId] = $carrierName;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'name_visible' => (string)($transport['name_visible'] ?? ''),
|
||||
'description' => (string)($transport['description'] ?? ''),
|
||||
'cost' => (float)($transport['cost'] ?? 0.0),
|
||||
'max_wp' => $transport['max_wp'] ?? '',
|
||||
'default' => (int)($transport['default'] ?? 0),
|
||||
'status' => (int)($transport['status'] ?? 0),
|
||||
'delivery_free' => (int)($transport['delivery_free'] ?? 0),
|
||||
'apilo_carrier_account_id' => $transport['apilo_carrier_account_id'] ?? '',
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'general',
|
||||
'readonly' => true,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::text('name_visible', [
|
||||
'label' => 'Nazwa widoczna',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::text('description', [
|
||||
'label' => 'Opis',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::number('cost', [
|
||||
'label' => 'Koszt (PLN)',
|
||||
'tab' => 'general',
|
||||
'step' => 0.01,
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::number('max_wp', [
|
||||
'label' => 'Maks. WP',
|
||||
'tab' => 'general',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('default', [
|
||||
'label' => 'Domyślna forma dostawy',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::switch('delivery_free', [
|
||||
'label' => 'Darmowa dostawa',
|
||||
'tab' => 'general',
|
||||
]),
|
||||
FormField::select('apilo_carrier_account_id', [
|
||||
'label' => 'Kurier z Apilo',
|
||||
'tab' => 'general',
|
||||
'options' => $apiloOptions,
|
||||
]),
|
||||
];
|
||||
|
||||
$transportPaymentMethods = $transport['payment_methods'] ?? [];
|
||||
|
||||
$paymentMethodsHtml = '';
|
||||
if (is_array($paymentMethods) && !empty($paymentMethods)) {
|
||||
foreach ($paymentMethods as $paymentMethod) {
|
||||
$pmId = (int)($paymentMethod['id'] ?? 0);
|
||||
$pmName = htmlspecialchars((string)($paymentMethod['name'] ?? ''), ENT_QUOTES, 'UTF-8');
|
||||
$pmStatus = (int)($paymentMethod['status'] ?? 0);
|
||||
$checked = in_array($pmId, $transportPaymentMethods) ? 'checked="checked"' : '';
|
||||
$statusClass = $pmStatus === 0 ? 'text-muted' : '';
|
||||
|
||||
$paymentMethodsHtml .= '<div class="form-group">';
|
||||
$paymentMethodsHtml .= '<div class="col-lg-12">';
|
||||
$paymentMethodsHtml .= '<div class="list">';
|
||||
$paymentMethodsHtml .= '<input type="checkbox" class="g-checkbox" name="payment_methods[]" value="' . $pmId . '" ' . $checked . ' />';
|
||||
$paymentMethodsHtml .= '<span class="bold ' . $statusClass . '">' . $pmName . '</span>';
|
||||
$paymentMethodsHtml .= '</div>';
|
||||
$paymentMethodsHtml .= '</div>';
|
||||
$paymentMethodsHtml .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$fields[] = FormField::custom(
|
||||
'payment_methods_section',
|
||||
$paymentMethodsHtml,
|
||||
['tab' => 'payment_methods']
|
||||
);
|
||||
|
||||
$tabs = [
|
||||
new FormTab('general', 'Ogólne', 'fa-file'),
|
||||
new FormTab('payment_methods', 'Powiązane metody płatności', 'fa-wrench'),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/shop_transport/save/id=' . $id;
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/shop_transport/list/'),
|
||||
FormAction::cancel('/admin/shop_transport/list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
'shop-transport-edit',
|
||||
'Edycja rodzaju transportu: ' . $name,
|
||||
$data,
|
||||
$fields,
|
||||
$tabs,
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/shop_transport/list/',
|
||||
true,
|
||||
['id' => $id]
|
||||
);
|
||||
}
|
||||
|
||||
private function getApiloCarrierAccounts(): array
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$integrationsRepository = new IntegrationsRepository( $mdb );
|
||||
$rawSetting = $integrationsRepository -> getSetting( 'apilo', 'carrier-account-list' );
|
||||
$raw = null;
|
||||
|
||||
if (is_array($rawSetting)) {
|
||||
$raw = $rawSetting;
|
||||
} elseif (is_string($rawSetting)) {
|
||||
$decoded = @unserialize($rawSetting);
|
||||
if (is_array($decoded)) {
|
||||
$raw = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($raw as $carrier) {
|
||||
if (is_array($carrier) && isset($carrier['id'], $carrier['name'])) {
|
||||
$list[(string)$carrier['id']] = (string)$carrier['name'];
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
49
autoload/admin/Controllers/UpdateController.php
Normal file
49
autoload/admin/Controllers/UpdateController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\Update\UpdateRepository;
|
||||
|
||||
class UpdateController
|
||||
{
|
||||
private UpdateRepository $repository;
|
||||
|
||||
public function __construct( UpdateRepository $repository )
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function main_view(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view( 'update/main-view', [
|
||||
'ver' => \Shared\Helpers\Helpers::get_version(),
|
||||
'new_ver' => \Shared\Helpers\Helpers::get_new_version(),
|
||||
] );
|
||||
}
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
$result = $this->repository->update();
|
||||
|
||||
if ( !$result['success'] ) {
|
||||
\Shared\Helpers\Helpers::alert( 'W trakcie aktualizacji systemu wystąpił błąd. Proszę spróbować ponownie.' );
|
||||
} else {
|
||||
\Shared\Helpers\Helpers::set_message( 'Aktualizacja przebiegła pomyślnie.' );
|
||||
}
|
||||
|
||||
header( 'Location: /admin/update/main_view/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public function updateAll(): void
|
||||
{
|
||||
$result = $this->repository->update();
|
||||
|
||||
$response = [
|
||||
'status' => !empty( $result['success'] ) && empty( $result['no_updates'] ),
|
||||
'version' => number_format( (float) \Shared\Helpers\Helpers::get( 'version_current' ) + 0.001, 3, '.', '' ),
|
||||
];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
346
autoload/admin/Controllers/UsersController.php
Normal file
346
autoload/admin/Controllers/UsersController.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
namespace admin\Controllers;
|
||||
|
||||
use Domain\User\UserRepository;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class UsersController
|
||||
{
|
||||
private UserRepository $repository;
|
||||
private FormRequestHandler $formHandler;
|
||||
|
||||
public function __construct(UserRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->formHandler = new FormRequestHandler();
|
||||
}
|
||||
|
||||
public function user_delete(): void
|
||||
{
|
||||
if ($this->repository->delete((int)\Shared\Helpers\Helpers::get('id'))) {
|
||||
\Shared\Helpers\Helpers::alert('Uzytkownik zostal usuniety.');
|
||||
}
|
||||
|
||||
header('Location: /admin/users/view_list/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public function user_save(): void
|
||||
{
|
||||
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
||||
if ($legacyValues) {
|
||||
$values = json_decode((string)$legacyValues, true);
|
||||
if (!is_array($values)) {
|
||||
echo json_encode(['status' => 'error', 'msg' => 'Nieprawidlowe dane formularza.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$this->isTwofaEmailValidForEnabled($values['twofa_enabled'] ?? 0, (string)($values['twofa_email'] ?? ''))) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'msg' => 'Jesli wlaczono dwustopniowe uwierzytelnianie (2FA), pole "E-mail do 2FA" jest wymagane.',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$response = $this->repository->save(
|
||||
(int)($values['id'] ?? 0),
|
||||
(string)($values['login'] ?? ''),
|
||||
$values['status'] ?? 0,
|
||||
(string)($values['password'] ?? ''),
|
||||
(string)($values['password_re'] ?? ''),
|
||||
$values['admin'] ?? 1,
|
||||
$values['twofa_enabled'] ?? 0,
|
||||
(string)($values['twofa_email'] ?? '')
|
||||
);
|
||||
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
$userId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
$user = $this->normalizeUser($this->repository->find($userId));
|
||||
$viewModel = $this->buildFormViewModel($user);
|
||||
|
||||
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
||||
if (!$result['success']) {
|
||||
$_SESSION['form_errors'][$this->getFormId()] = $result['errors'];
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$data['id'] = $userId;
|
||||
$data['admin'] = 1;
|
||||
|
||||
if (!$this->isTwofaEmailValidForEnabled($data['twofa_enabled'] ?? 0, (string)($data['twofa_email'] ?? ''))) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'twofa_email' => 'Pole "E-mail do 2FA" jest wymagane, gdy wlaczono 2FA.',
|
||||
],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$duplicateLoginCheck = $this->repository->checkLogin((string)($data['login'] ?? ''), $userId);
|
||||
if (($duplicateLoginCheck['status'] ?? '') !== 'ok') {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'errors' => ['login' => (string)($duplicateLoginCheck['msg'] ?? 'Podany login jest juz zajety.')],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$response = $this->repository->save(
|
||||
(int)$data['id'],
|
||||
(string)($data['login'] ?? ''),
|
||||
(int)($data['status'] ?? 0),
|
||||
(string)($data['password'] ?? ''),
|
||||
(string)($data['password_re'] ?? ''),
|
||||
1,
|
||||
(int)($data['twofa_enabled'] ?? 0),
|
||||
(string)($data['twofa_email'] ?? '')
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'success' => ($response['status'] ?? '') === 'ok',
|
||||
'message' => (string)($response['msg'] ?? 'Zmiany zostaly zapisane.'),
|
||||
'errors' => (($response['status'] ?? '') === 'ok') ? [] : ['general' => (string)($response['msg'] ?? 'Wystapil blad.')],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function user_edit(): string
|
||||
{
|
||||
$user = $this->normalizeUser($this->repository->find((int)\Shared\Helpers\Helpers::get('id')));
|
||||
$validationErrors = $_SESSION['form_errors'][$this->getFormId()] ?? null;
|
||||
if ($validationErrors) {
|
||||
unset($_SESSION['form_errors'][$this->getFormId()]);
|
||||
}
|
||||
|
||||
return \Shared\Tpl\Tpl::view('users/user-edit', [
|
||||
'form' => $this->buildFormViewModel($user, $validationErrors),
|
||||
]);
|
||||
}
|
||||
|
||||
public function view_list(): string
|
||||
{
|
||||
$sortableColumns = ['login', 'status'];
|
||||
$filterDefinitions = [
|
||||
[
|
||||
'key' => 'login',
|
||||
'label' => 'Login',
|
||||
'type' => 'text',
|
||||
],
|
||||
[
|
||||
'key' => 'status',
|
||||
'label' => 'Aktywny',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'' => '- aktywny -',
|
||||
'1' => 'tak',
|
||||
'0' => 'nie',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$listRequest = \admin\Support\TableListRequestFactory::fromRequest(
|
||||
$filterDefinitions,
|
||||
$sortableColumns,
|
||||
'login'
|
||||
);
|
||||
|
||||
$sortDir = $listRequest['sortDir'];
|
||||
if (trim((string)\Shared\Helpers\Helpers::get('sort')) === '') {
|
||||
$sortDir = 'ASC';
|
||||
}
|
||||
|
||||
$result = $this->repository->listForAdmin(
|
||||
$listRequest['filters'],
|
||||
$listRequest['sortColumn'],
|
||||
$sortDir,
|
||||
$listRequest['page'],
|
||||
$listRequest['perPage']
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1;
|
||||
foreach ($result['items'] as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$login = trim((string)($item['login'] ?? ''));
|
||||
$status = (int)($item['status'] ?? 0);
|
||||
|
||||
$rows[] = [
|
||||
'lp' => $lp++ . '.',
|
||||
'status' => $status === 1 ? 'tak' : '<span style="color: #FF0000;">nie</span>',
|
||||
'login' => '<a href="/admin/users/user_edit/id=' . $id . '">' . htmlspecialchars($login, ENT_QUOTES, 'UTF-8') . '</a>',
|
||||
'_actions' => [
|
||||
[
|
||||
'label' => 'Edytuj',
|
||||
'url' => '/admin/users/user_edit/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-primary',
|
||||
],
|
||||
[
|
||||
'label' => 'Usun',
|
||||
'url' => '/admin/users/user_delete/id=' . $id,
|
||||
'class' => 'btn btn-xs btn-danger',
|
||||
'confirm' => 'Na pewno chcesz usunac wybranego uzytkownika?',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$total = (int)$result['total'];
|
||||
$totalPages = max(1, (int)ceil($total / $listRequest['perPage']));
|
||||
|
||||
$viewModel = new \admin\ViewModels\Common\PaginatedTableViewModel(
|
||||
[
|
||||
['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false],
|
||||
['key' => 'status', 'sort_key' => 'status', 'label' => 'Aktywny', 'class' => 'text-center', 'sortable' => true, 'raw' => true],
|
||||
['key' => 'login', 'sort_key' => 'login', 'label' => 'Login', 'sortable' => true, 'raw' => true],
|
||||
],
|
||||
$rows,
|
||||
$listRequest['viewFilters'],
|
||||
[
|
||||
'column' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
],
|
||||
[
|
||||
'page' => $listRequest['page'],
|
||||
'per_page' => $listRequest['perPage'],
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
],
|
||||
array_merge($listRequest['queryFilters'], [
|
||||
'sort' => $listRequest['sortColumn'],
|
||||
'dir' => $sortDir,
|
||||
'per_page' => $listRequest['perPage'],
|
||||
]),
|
||||
$listRequest['perPageOptions'],
|
||||
$sortableColumns,
|
||||
'/admin/users/view_list/',
|
||||
'Brak danych w tabeli.',
|
||||
'/admin/users/user_edit/',
|
||||
'Dodaj uzytkownika'
|
||||
);
|
||||
|
||||
return \Shared\Tpl\Tpl::view('users/users-list', [
|
||||
'viewModel' => $viewModel,
|
||||
]);
|
||||
}
|
||||
|
||||
public function list(): string
|
||||
{
|
||||
return $this->view_list();
|
||||
}
|
||||
|
||||
public function login_form(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('site/unlogged-layout');
|
||||
}
|
||||
|
||||
public function twofa(): string
|
||||
{
|
||||
return \Shared\Tpl\Tpl::view('site/unlogged', [
|
||||
'content' => \Shared\Tpl\Tpl::view('users/user-2fa'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function normalizeUser(?array $user): array
|
||||
{
|
||||
return [
|
||||
'id' => (int)($user['id'] ?? 0),
|
||||
'login' => (string)($user['login'] ?? ''),
|
||||
'status' => (int)($user['status'] ?? 1),
|
||||
'admin' => (int)($user['admin'] ?? 1),
|
||||
'twofa_enabled' => (int)($user['twofa_enabled'] ?? 0),
|
||||
'twofa_email' => (string)($user['twofa_email'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function buildFormViewModel(array $user, ?array $errors = null): FormEditViewModel
|
||||
{
|
||||
$userId = (int)($user['id'] ?? 0);
|
||||
$isNew = $userId <= 0;
|
||||
|
||||
$data = [
|
||||
'id' => $userId,
|
||||
'login' => (string)($user['login'] ?? ''),
|
||||
'status' => (int)($user['status'] ?? 1),
|
||||
'twofa_enabled' => (int)($user['twofa_enabled'] ?? 0),
|
||||
'twofa_email' => (string)($user['twofa_email'] ?? ''),
|
||||
'password' => '',
|
||||
'password_re' => '',
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::text('login', [
|
||||
'label' => 'Login',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
]),
|
||||
FormField::switch('twofa_enabled', [
|
||||
'label' => 'Dwustopniowe uwierzytelnianie (2FA)',
|
||||
]),
|
||||
FormField::email('twofa_email', [
|
||||
'label' => 'E-mail do 2FA',
|
||||
]),
|
||||
FormField::password('password', [
|
||||
'label' => 'Haslo',
|
||||
'required' => $isNew,
|
||||
'attributes' => ['minlength' => 5],
|
||||
]),
|
||||
FormField::password('password_re', [
|
||||
'label' => 'Haslo - powtorz',
|
||||
'required' => $isNew,
|
||||
'attributes' => ['minlength' => 5],
|
||||
]),
|
||||
];
|
||||
|
||||
$actionUrl = '/admin/users/user_save/' . ($isNew ? '' : ('id=' . $userId));
|
||||
$actions = [
|
||||
FormAction::save($actionUrl, '/admin/users/view_list/'),
|
||||
FormAction::cancel('/admin/users/view_list/'),
|
||||
];
|
||||
|
||||
return new FormEditViewModel(
|
||||
$this->getFormId(),
|
||||
$isNew ? 'Nowy uzytkownik' : 'Edycja uzytkownika',
|
||||
$data,
|
||||
$fields,
|
||||
[],
|
||||
$actions,
|
||||
'POST',
|
||||
$actionUrl,
|
||||
'/admin/users/view_list/',
|
||||
true,
|
||||
[
|
||||
'id' => $userId,
|
||||
'admin' => 1,
|
||||
],
|
||||
null,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
private function getFormId(): string
|
||||
{
|
||||
return 'users-edit';
|
||||
}
|
||||
|
||||
private function isTwofaEmailValidForEnabled($twofaEnabled, string $twofaEmail): bool
|
||||
{
|
||||
$enabled = ($twofaEnabled === 'on' || $twofaEnabled === 1 || $twofaEnabled === '1' || $twofaEnabled === true);
|
||||
if (!$enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return trim($twofaEmail) !== '';
|
||||
}
|
||||
}
|
||||
494
autoload/admin/Support/Forms/FormFieldRenderer.php
Normal file
494
autoload/admin/Support/Forms/FormFieldRenderer.php
Normal file
@@ -0,0 +1,494 @@
|
||||
<?php
|
||||
namespace admin\Support\Forms;
|
||||
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormFieldType;
|
||||
|
||||
/**
|
||||
* Renderer pól formularza
|
||||
*/
|
||||
class FormFieldRenderer
|
||||
{
|
||||
private FormEditViewModel $form;
|
||||
|
||||
public function __construct(FormEditViewModel $form)
|
||||
{
|
||||
$this->form = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pojedyncze pole
|
||||
*/
|
||||
public function renderField(FormField $field): string
|
||||
{
|
||||
$method = 'render' . ucfirst($field->type);
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->$method($field);
|
||||
}
|
||||
|
||||
// Fallback dla nieznanych typów - renderuj jako text
|
||||
return $this->renderText($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole tekstowe
|
||||
*/
|
||||
public function renderText(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
|
||||
$params = [
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
];
|
||||
|
||||
if ($field->placeholder) {
|
||||
$params['placeholder'] = $field->placeholder;
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
$params['class'] .= ' error';
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input($params), $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole number
|
||||
*/
|
||||
public function renderNumber(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
|
||||
$params = [
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'number',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
];
|
||||
|
||||
if ($error) {
|
||||
$params['class'] .= ' error';
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input($params), $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole email
|
||||
*/
|
||||
public function renderEmail(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
|
||||
$params = [
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'email',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
];
|
||||
|
||||
if ($error) {
|
||||
$params['class'] .= ' error';
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input($params), $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole password
|
||||
*/
|
||||
public function renderPassword(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
return \Shared\Html\Html::input([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'password',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole daty
|
||||
*/
|
||||
public function renderDate(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
|
||||
$params = [
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'class' => 'date ' . ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
];
|
||||
|
||||
if ($error) {
|
||||
$params['class'] .= ' error';
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input($params), $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole daty i czasu
|
||||
*/
|
||||
public function renderDatetime(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
return \Shared\Html\Html::input([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'class' => 'datetime ' . ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje przełącznik (switch)
|
||||
*/
|
||||
public function renderSwitch(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
// Domyślna wartość dla nowego rekordu
|
||||
if ($value === null && $field->value === true) {
|
||||
$checked = true;
|
||||
} else {
|
||||
$checked = (bool) $value;
|
||||
}
|
||||
|
||||
return \Shared\Html\Html::input_switch([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'checked' => $checked,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje select
|
||||
*/
|
||||
public function renderSelect(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
|
||||
$params = [
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'values' => $field->options,
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
];
|
||||
|
||||
if ($error) {
|
||||
$params['class'] .= ' error';
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::select($params), $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje textarea
|
||||
*/
|
||||
public function renderTextarea(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
return \Shared\Html\Html::textarea([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'rows' => $field->attributes['rows'] ?? 4,
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje edytor (CKEditor)
|
||||
*/
|
||||
public function renderEditor(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
return \Shared\Html\Html::textarea([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'rows' => max(10, ($field->attributes['rows'] ?? 10)),
|
||||
'class' => 'editor ' . ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole obrazu z filemanagerem
|
||||
*/
|
||||
public function renderImage(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
$filemanagerUrl = $field->filemanagerUrl ?? $this->generateFilemanagerUrl($field->id);
|
||||
|
||||
return \Shared\Html\Html::input_icon([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'icon_content' => 'przeglądaj',
|
||||
'icon_js' => "window.open('{$filemanagerUrl}', 'filemanager', 'location=1,status=1,scrollbars=1,width=1100,height=700')",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole pliku
|
||||
*/
|
||||
public function renderFile(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
if ($field->useFilemanager) {
|
||||
$filemanagerUrl = $field->filemanagerUrl ?? $this->generateFilemanagerUrl($field->id);
|
||||
|
||||
return \Shared\Html\Html::input_icon([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'icon_content' => 'przeglądaj',
|
||||
'icon_js' => "window.open('{$filemanagerUrl}', 'filemanager', 'location=1,status=1,scrollbars=1,width=1100,height=700')",
|
||||
]);
|
||||
}
|
||||
|
||||
return \Shared\Html\Html::input([
|
||||
'label' => $field->label,
|
||||
'name' => $field->name,
|
||||
'id' => $field->id,
|
||||
'type' => 'file',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje ukryte pole
|
||||
*/
|
||||
public function renderHidden(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
|
||||
return '<input type="hidden" name="' . htmlspecialchars($field->name) . '" ' .
|
||||
'id="' . htmlspecialchars($field->id) . '" ' .
|
||||
'value="' . htmlspecialchars($value ?? '') . '">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole koloru (color picker + text input)
|
||||
*/
|
||||
public function renderColor(FormField $field): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field);
|
||||
$error = $this->form->getError($field->name);
|
||||
$colorValue = htmlspecialchars($value ?? '#000000', ENT_QUOTES, 'UTF-8');
|
||||
$fieldName = htmlspecialchars($field->name, ENT_QUOTES, 'UTF-8');
|
||||
$fieldId = htmlspecialchars($field->id, ENT_QUOTES, 'UTF-8');
|
||||
$label = htmlspecialchars($field->label, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$html = '<div class="form-group row">';
|
||||
$html .= '<label class="col-lg-4 control-label">' . $label . ':</label>';
|
||||
$html .= '<div class="col-lg-8">';
|
||||
$html .= '<div style="display:flex;align-items:center;gap:8px;">';
|
||||
$html .= '<input type="color" id="' . $fieldId . '_picker" value="' . $colorValue . '" style="width:40px;height:34px;padding:2px;border:1px solid #ccc;cursor:pointer;" />';
|
||||
$html .= '<input type="text" name="' . $fieldName . '" id="' . $fieldId . '" value="' . $colorValue . '" class="form-control" style="max-width:150px;" />';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
$html .= '<script>$(function(){'
|
||||
. 'var $p=$("#' . $fieldId . '_picker"),$t=$("#' . $fieldId . '");'
|
||||
. '$p.on("input",function(){$t.val(this.value);});'
|
||||
. '$t.on("input",function(){var v=this.value;if(/^#[0-9a-fA-F]{6}$/.test(v))$p.val(v);});'
|
||||
. '});</script>';
|
||||
|
||||
return $this->wrapWithError($html, $error);
|
||||
}
|
||||
|
||||
public function renderCustom(FormField $field): string
|
||||
{
|
||||
return (string)($field->customHtml ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje sekcję językową
|
||||
*/
|
||||
public function renderLangSection(FormField $section): string
|
||||
{
|
||||
if ($section->langFields === null || $this->form->languages === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$out = '<div id="languages-' . $section->name . '" class="languages-tabs">';
|
||||
|
||||
// Zakładki języków
|
||||
$out .= '<ul class="resp-tabs-list languages-tabs htabs">';
|
||||
foreach ($this->form->languages as $lang) {
|
||||
if ($lang['status']) {
|
||||
$out .= '<li>' . htmlspecialchars($lang['name']) . '</li>';
|
||||
}
|
||||
}
|
||||
$out .= '</ul>';
|
||||
|
||||
// Kontenery języków
|
||||
$out .= '<div class="resp-tabs-container languages-tabs">';
|
||||
foreach ($this->form->languages as $lang) {
|
||||
if ($lang['status']) {
|
||||
$out .= '<div>';
|
||||
foreach ($section->langFields as $field) {
|
||||
$out .= $this->renderLangField($field, $lang['id'], $section->name);
|
||||
}
|
||||
$out .= '</div>';
|
||||
}
|
||||
}
|
||||
$out .= '</div>';
|
||||
|
||||
$out .= '</div>';
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderuje pole w sekcji językowej
|
||||
*/
|
||||
private function renderLangField(FormField $field, $languageId, string $sectionName): string
|
||||
{
|
||||
$value = $this->form->getFieldValue($field, $languageId, $field->name);
|
||||
$error = $this->form->getError($sectionName . '_' . $field->name, $languageId);
|
||||
|
||||
$name = $field->getLocalizedName($languageId);
|
||||
$id = $field->getLocalizedId($languageId);
|
||||
|
||||
switch ($field->type) {
|
||||
case FormFieldType::IMAGE:
|
||||
$filemanagerUrl = $field->filemanagerUrl ?? $this->generateFilemanagerUrl($id);
|
||||
return $this->wrapWithError(\Shared\Html\Html::input_icon([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'value' => $value ?? '',
|
||||
'type' => 'text',
|
||||
'icon_content' => 'przeglądaj',
|
||||
'icon_js' => "window.open('{$filemanagerUrl}', 'filemanager', 'location=1,status=1,scrollbars=1,width=1100,height=700')",
|
||||
]), $error);
|
||||
|
||||
case FormFieldType::TEXTAREA:
|
||||
case FormFieldType::EDITOR:
|
||||
return $this->wrapWithError(\Shared\Html\Html::textarea([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'value' => $value ?? '',
|
||||
'rows' => $field->type === FormFieldType::EDITOR ? 10 : ($field->attributes['rows'] ?? 4),
|
||||
'class' => $field->type === FormFieldType::EDITOR ? 'editor' : '',
|
||||
]), $error);
|
||||
|
||||
case FormFieldType::SWITCH:
|
||||
return \Shared\Html\Html::input_switch([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'checked' => (bool) $value,
|
||||
]);
|
||||
|
||||
case FormFieldType::SELECT:
|
||||
return $this->wrapWithError(\Shared\Html\Html::select([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'value' => $value ?? '',
|
||||
'values' => $field->options,
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]), $error);
|
||||
|
||||
default: // TEXT, URL, etc.
|
||||
if (!empty($field->attributes['icon_content'])) {
|
||||
$iconJs = (string)($field->attributes['icon_js'] ?? '');
|
||||
if ($iconJs !== '') {
|
||||
$iconJs = str_replace('{lang}', (string)$languageId, $iconJs);
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input_icon([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'value' => $value ?? '',
|
||||
'type' => $field->type === FormFieldType::EMAIL ? 'email' : 'text',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
'icon_content' => (string)$field->attributes['icon_content'],
|
||||
'icon_class' => (string)($field->attributes['icon_class'] ?? ''),
|
||||
'icon_js' => $iconJs,
|
||||
]), $error);
|
||||
}
|
||||
|
||||
return $this->wrapWithError(\Shared\Html\Html::input([
|
||||
'label' => $field->label,
|
||||
'name' => $name,
|
||||
'id' => $id,
|
||||
'value' => $value ?? '',
|
||||
'type' => $field->type === FormFieldType::EMAIL ? 'email' : 'text',
|
||||
'class' => ($field->required ? 'require ' : '') . ($field->attributes['class'] ?? ''),
|
||||
]), $error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generuje URL do filemanagera
|
||||
*/
|
||||
private function generateFilemanagerUrl(string $fieldId): string
|
||||
{
|
||||
$rfmAkey = $_SESSION['rfm_akey'] ?? bin2hex(random_bytes(16));
|
||||
$_SESSION['rfm_akey'] = $rfmAkey;
|
||||
$_SESSION['rfm_akey_expires'] = time() + 20 * 60;
|
||||
$_SESSION['can_use_rfm'] = true;
|
||||
|
||||
$fieldIdParam = rawurlencode($fieldId);
|
||||
$akeyParam = rawurlencode($rfmAkey);
|
||||
return "/libraries/filemanager-9.14.2/dialog.php?type=1&popup=1&field_id={$fieldIdParam}&akey={$akeyParam}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Opakowuje pole w kontener błędu
|
||||
*/
|
||||
private function wrapWithError(string $html, ?string $error): string
|
||||
{
|
||||
if ($error) {
|
||||
return '<div class="field-with-error">' . $html .
|
||||
'<span class="error-message">' . htmlspecialchars($error) . '</span></div>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
152
autoload/admin/Support/Forms/FormRequestHandler.php
Normal file
152
autoload/admin/Support/Forms/FormRequestHandler.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
namespace admin\Support\Forms;
|
||||
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormFieldType;
|
||||
use admin\Validation\FormValidator;
|
||||
|
||||
/**
|
||||
* Obsługa żądań formularza (POST, persist, walidacja)
|
||||
*/
|
||||
class FormRequestHandler
|
||||
{
|
||||
private FormValidator $validator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->validator = new FormValidator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Przetwarza żądanie POST formularza
|
||||
*
|
||||
* @param FormEditViewModel $formViewModel
|
||||
* @param array $postData Dane z $_POST
|
||||
* @return array Wynik przetwarzania ['success' => bool, 'errors' => array, 'data' => array]
|
||||
*/
|
||||
public function handleSubmit(FormEditViewModel $formViewModel, array $postData): array
|
||||
{
|
||||
$result = [
|
||||
'success' => false,
|
||||
'errors' => [],
|
||||
'data' => []
|
||||
];
|
||||
|
||||
// Walidacja
|
||||
$errors = $this->validator->validate($postData, $formViewModel->fields, $formViewModel->languages);
|
||||
|
||||
if (!empty($errors)) {
|
||||
$result['errors'] = $errors;
|
||||
// Zapisz dane do persist przy błędzie walidacji
|
||||
if ($formViewModel->persist) {
|
||||
$formViewModel->saveToPersist($postData);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Przetwórz dane (np. konwersja typów)
|
||||
$processedData = $this->processData($postData, $formViewModel->fields);
|
||||
|
||||
$result['success'] = true;
|
||||
$result['data'] = $processedData;
|
||||
|
||||
// Wyczyść persist po sukcesie
|
||||
if ($formViewModel->persist) {
|
||||
$formViewModel->clearPersist();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Przetwarza dane z formularza (konwersja typów)
|
||||
*/
|
||||
private function processData(array $postData, array $fields): array
|
||||
{
|
||||
$processed = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$value = $postData[$field->name] ?? null;
|
||||
|
||||
// Konwersja typów
|
||||
switch ($field->type) {
|
||||
case FormFieldType::SWITCH:
|
||||
$processed[$field->name] = $value ? 1 : 0;
|
||||
break;
|
||||
|
||||
case FormFieldType::NUMBER:
|
||||
$processed[$field->name] = $value !== null && $value !== '' ? (float)$value : null;
|
||||
break;
|
||||
|
||||
case FormFieldType::LANG_SECTION:
|
||||
if ($field->langFields !== null) {
|
||||
$processed[$field->name] = $this->processLangSection($postData, $field);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$processed[$field->name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Przetwarza sekcję językową
|
||||
*/
|
||||
private function processLangSection(array $postData, $section): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if ($section->langFields === null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($section->langFields as $field) {
|
||||
$fieldName = $field->name;
|
||||
$langData = $postData[$fieldName] ?? [];
|
||||
|
||||
foreach ($langData as $langId => $value) {
|
||||
if (!isset($result[$langId])) {
|
||||
$result[$langId] = [];
|
||||
}
|
||||
|
||||
// Konwersja typów dla pól językowych
|
||||
switch ($field->type) {
|
||||
case FormFieldType::SWITCH:
|
||||
$result[$langId][$fieldName] = $value ? 1 : 0;
|
||||
break;
|
||||
case FormFieldType::NUMBER:
|
||||
$result[$langId][$fieldName] = $value !== null && $value !== '' ? (float)$value : null;
|
||||
break;
|
||||
default:
|
||||
$result[$langId][$fieldName] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Przywraca dane z persist do POST (przy błędzie walidacji)
|
||||
*/
|
||||
public function restoreFromPersist(FormEditViewModel $formViewModel): ?array
|
||||
{
|
||||
if (!$formViewModel->persist) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $_SESSION['form_persist'][$formViewModel->formId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy żądanie jest submitowaniem formularza
|
||||
*/
|
||||
public function isFormSubmit(string $formId): bool
|
||||
{
|
||||
return $_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||||
(isset($_POST['_form_id']) && $_POST['_form_id'] === $formId);
|
||||
}
|
||||
}
|
||||
100
autoload/admin/Support/class.TableListRequestFactory.php
Normal file
100
autoload/admin/Support/class.TableListRequestFactory.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace admin\Support;
|
||||
|
||||
class TableListRequestFactory
|
||||
{
|
||||
public const DEFAULT_PER_PAGE_OPTIONS = [5, 10, 15, 25, 50, 100];
|
||||
public const DEFAULT_PER_PAGE = 15;
|
||||
|
||||
/**
|
||||
* Buduje kontekst listy (filtry, sortowanie, paginacja) z requestu.
|
||||
*
|
||||
* @return array{
|
||||
* page:int,
|
||||
* perPage:int,
|
||||
* perPageOptions:array<int,int>,
|
||||
* filters:array<string,string>,
|
||||
* viewFilters:array<int,array<string,mixed>>,
|
||||
* queryFilters:array<string,string>,
|
||||
* sortColumn:string,
|
||||
* sortDir:string
|
||||
* }
|
||||
*/
|
||||
public static function fromRequest(
|
||||
array $filterDefinitions,
|
||||
array $sortableColumns,
|
||||
string $defaultSortColumn = 'date_add',
|
||||
?array $perPageOptions = null,
|
||||
?int $defaultPerPage = null
|
||||
): array {
|
||||
if ($perPageOptions === null) {
|
||||
$perPageOptions = self::DEFAULT_PER_PAGE_OPTIONS;
|
||||
}
|
||||
|
||||
if ($defaultPerPage === null) {
|
||||
$defaultPerPage = self::DEFAULT_PER_PAGE;
|
||||
}
|
||||
|
||||
if (!in_array($defaultPerPage, $perPageOptions, true)) {
|
||||
$defaultPerPage = (int)$perPageOptions[0];
|
||||
}
|
||||
|
||||
$page = max(1, (int)\Shared\Helpers\Helpers::get('page'));
|
||||
$perPage = (int)\Shared\Helpers\Helpers::get('per_page');
|
||||
if (!in_array($perPage, $perPageOptions, true)) {
|
||||
$perPage = $defaultPerPage;
|
||||
}
|
||||
|
||||
$filters = [];
|
||||
$viewFilters = [];
|
||||
$queryFilters = [];
|
||||
|
||||
foreach ($filterDefinitions as $definition) {
|
||||
$key = (string)($definition['key'] ?? '');
|
||||
if ($key === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = (string)($definition['type'] ?? 'text');
|
||||
$value = (string)\Shared\Helpers\Helpers::get($key);
|
||||
|
||||
$filters[$key] = $value;
|
||||
$queryFilters[$key] = $value;
|
||||
|
||||
$filterConfig = [
|
||||
'key' => $key,
|
||||
'label' => (string)($definition['label'] ?? $key),
|
||||
'type' => $type,
|
||||
'value' => $value,
|
||||
];
|
||||
|
||||
if ($type === 'select' && isset($definition['options']) && is_array($definition['options'])) {
|
||||
$filterConfig['options'] = $definition['options'];
|
||||
}
|
||||
|
||||
$viewFilters[] = $filterConfig;
|
||||
}
|
||||
|
||||
$sortColumn = trim((string)\Shared\Helpers\Helpers::get('sort'));
|
||||
if (!in_array($sortColumn, $sortableColumns, true)) {
|
||||
$sortColumn = $defaultSortColumn;
|
||||
}
|
||||
|
||||
$sortDir = strtoupper(trim((string)\Shared\Helpers\Helpers::get('dir')));
|
||||
if (!in_array($sortDir, ['ASC', 'DESC'], true)) {
|
||||
$sortDir = 'DESC';
|
||||
}
|
||||
|
||||
return [
|
||||
'page' => $page,
|
||||
'perPage' => $perPage,
|
||||
'perPageOptions' => $perPageOptions,
|
||||
'filters' => $filters,
|
||||
'viewFilters' => $viewFilters,
|
||||
'queryFilters' => $queryFilters,
|
||||
'sortColumn' => $sortColumn,
|
||||
'sortDir' => $sortDir,
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
196
autoload/admin/Validation/FormValidator.php
Normal file
196
autoload/admin/Validation/FormValidator.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
namespace admin\Validation;
|
||||
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormFieldType;
|
||||
|
||||
/**
|
||||
* Walidator formularzy
|
||||
*/
|
||||
class FormValidator
|
||||
{
|
||||
private array $errors = [];
|
||||
|
||||
/**
|
||||
* Waliduje dane na podstawie definicji pól
|
||||
*
|
||||
* @param array $data Dane z POST
|
||||
* @param array $fields Definicje pól (FormField[])
|
||||
* @param array|null $languages Języki (dla walidacji pól językowych)
|
||||
* @return array Tablica błędów (pusta jeśli OK)
|
||||
*/
|
||||
public function validate(array $data, array $fields, ?array $languages = null): array
|
||||
{
|
||||
$this->errors = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->type === FormFieldType::LANG_SECTION) {
|
||||
$this->validateLangSection($data, $field, $languages ?? []);
|
||||
} else {
|
||||
$this->validateField($data, $field);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waliduje pojedyncze pole
|
||||
*/
|
||||
private function validateField(array $data, FormField $field): void
|
||||
{
|
||||
$value = $data[$field->name] ?? null;
|
||||
|
||||
// Walidacja wymagalności
|
||||
if ($field->required && $this->isEmpty($value)) {
|
||||
$this->errors[$field->name] = "Pole \"{$field->label}\" jest wymagane.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Jeśli pole puste i nie jest wymagane - pomijamy dalszą walidację
|
||||
if ($this->isEmpty($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walidacja typu
|
||||
switch ($field->type) {
|
||||
case FormFieldType::EMAIL:
|
||||
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->errors[$field->name] = "Pole \"{$field->label}\" musi być poprawnym adresem e-mail.";
|
||||
}
|
||||
break;
|
||||
|
||||
case FormFieldType::NUMBER:
|
||||
if (!is_numeric($value)) {
|
||||
$this->errors[$field->name] = "Pole \"{$field->label}\" musi być liczbą.";
|
||||
}
|
||||
break;
|
||||
|
||||
case FormFieldType::DATE:
|
||||
if (!$this->isValidDate($value)) {
|
||||
$this->errors[$field->name] = "Pole \"{$field->label}\" musi być poprawną datą (YYYY-MM-DD).";
|
||||
}
|
||||
break;
|
||||
|
||||
case FormFieldType::DATETIME:
|
||||
if (!$this->isValidDateTime($value)) {
|
||||
$this->errors[$field->name] = "Pole \"{$field->label}\" musi być poprawną datą i czasem.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Walidacja customowa (callback)
|
||||
if (isset($field->attributes['validate_callback']) && is_callable($field->attributes['validate_callback'])) {
|
||||
$result = call_user_func($field->attributes['validate_callback'], $value, $data);
|
||||
if ($result !== true) {
|
||||
$this->errors[$field->name] = is_string($result) ? $result : "Pole \"{$field->label}\" zawiera nieprawidłową wartość.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waliduje sekcję językową
|
||||
*/
|
||||
private function validateLangSection(array $data, FormField $section, array $languages): void
|
||||
{
|
||||
if ($section->langFields === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($languages as $language) {
|
||||
if (!($language['status'] ?? false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$langId = $language['id'];
|
||||
|
||||
foreach ($section->langFields as $field) {
|
||||
$fieldName = $field->name;
|
||||
$value = $data[$fieldName][$langId] ?? null;
|
||||
|
||||
// Walidacja wymagalności
|
||||
if ($field->required && $this->isEmpty($value)) {
|
||||
$errorKey = "{$section->name}_{$fieldName}";
|
||||
$this->errors[$errorKey][$langId] = "Pole \"{$field->label}\" ({$language['name']}) jest wymagane.";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Walidacja typu dla pól językowych
|
||||
if (!$this->isEmpty($value)) {
|
||||
switch ($field->type) {
|
||||
case FormFieldType::EMAIL:
|
||||
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
$errorKey = "{$section->name}_{$fieldName}";
|
||||
$this->errors[$errorKey][$langId] = "Pole \"{$field->label}\" ({$language['name']}) musi być poprawnym e-mailem.";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy wartość jest pusta
|
||||
*/
|
||||
private function isEmpty($value): bool
|
||||
{
|
||||
return $value === null || $value === '' || (is_array($value) && empty($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy data jest poprawna (YYYY-MM-DD)
|
||||
*/
|
||||
private function isValidDate(string $date): bool
|
||||
{
|
||||
$d = \DateTime::createFromFormat('Y-m-d', $date);
|
||||
return $d && $d->format('Y-m-d') === $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy data i czas są poprawne
|
||||
*/
|
||||
private function isValidDateTime(string $datetime): bool
|
||||
{
|
||||
$d = \DateTime::createFromFormat('Y-m-d H:i:s', $datetime);
|
||||
if ($d && $d->format('Y-m-d H:i:s') === $datetime) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Spróbuj bez sekund
|
||||
$d = \DateTime::createFromFormat('Y-m-d H:i', $datetime);
|
||||
return $d && $d->format('Y-m-d H:i') === $datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy walidacja zakończyła się sukcesem
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca wszystkie błędy
|
||||
*/
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca pierwszy błąd
|
||||
*/
|
||||
public function getFirstError(): ?string
|
||||
{
|
||||
if (empty($this->errors)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$first = reset($this->errors);
|
||||
if (is_array($first)) {
|
||||
return reset($first);
|
||||
}
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Common;
|
||||
|
||||
class PaginatedTableViewModel
|
||||
{
|
||||
public array $columns;
|
||||
public array $rows;
|
||||
public array $filters;
|
||||
public array $sort;
|
||||
public array $pagination;
|
||||
public array $query;
|
||||
public array $perPageOptions;
|
||||
public array $sortableColumns;
|
||||
public string $basePath;
|
||||
public string $emptyMessage;
|
||||
public ?string $createUrl;
|
||||
public ?string $createLabel;
|
||||
public ?string $customScriptView;
|
||||
|
||||
public function __construct(
|
||||
array $columns = [],
|
||||
array $rows = [],
|
||||
array $filters = [],
|
||||
array $sort = [],
|
||||
array $pagination = [],
|
||||
array $query = [],
|
||||
array $perPageOptions = [5, 10, 15, 25, 50, 100],
|
||||
array $sortableColumns = [],
|
||||
string $basePath = '',
|
||||
string $emptyMessage = 'Brak danych.',
|
||||
?string $createUrl = null,
|
||||
?string $createLabel = null,
|
||||
?string $customScriptView = null
|
||||
) {
|
||||
$this->columns = $columns;
|
||||
$this->rows = $rows;
|
||||
$this->filters = $filters;
|
||||
$this->sort = $sort;
|
||||
$this->pagination = $pagination;
|
||||
$this->query = $query;
|
||||
$this->perPageOptions = $perPageOptions;
|
||||
$this->sortableColumns = $sortableColumns;
|
||||
$this->basePath = $basePath;
|
||||
$this->emptyMessage = $emptyMessage;
|
||||
$this->createUrl = $createUrl;
|
||||
$this->createLabel = $createLabel;
|
||||
$this->customScriptView = $customScriptView;
|
||||
}
|
||||
}
|
||||
?>
|
||||
73
autoload/admin/ViewModels/Forms/FormAction.php
Normal file
73
autoload/admin/ViewModels/Forms/FormAction.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Forms;
|
||||
|
||||
/**
|
||||
* Definicja akcji formularza (przycisku)
|
||||
*/
|
||||
class FormAction
|
||||
{
|
||||
public string $name;
|
||||
public string $label;
|
||||
public string $type;
|
||||
public string $url;
|
||||
public ?string $backUrl;
|
||||
public string $cssClass;
|
||||
public array $attributes;
|
||||
|
||||
/**
|
||||
* @param string $name Nazwa akcji (save, cancel, delete)
|
||||
* @param string $label Etykieta przycisku
|
||||
* @param string $url URL akcji (dla save)
|
||||
* @param string|null $backUrl URL powrotu po zapisie
|
||||
* @param string $cssClass Klasy CSS przycisku
|
||||
* @param string $type Typ przycisku (submit, button, link)
|
||||
* @param array $attributes Dodatkowe atrybuty HTML
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $label,
|
||||
string $url = '',
|
||||
?string $backUrl = null,
|
||||
string $cssClass = 'btn btn-primary',
|
||||
string $type = 'submit',
|
||||
array $attributes = []
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->label = $label;
|
||||
$this->url = $url;
|
||||
$this->backUrl = $backUrl;
|
||||
$this->cssClass = $cssClass;
|
||||
$this->type = $type;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Predefiniowana akcja Zapisz
|
||||
*/
|
||||
public static function save(string $url, string $backUrl = '', string $label = 'Zapisz'): self
|
||||
{
|
||||
return new self(
|
||||
'save',
|
||||
$label,
|
||||
$url,
|
||||
$backUrl,
|
||||
'btn btn-primary',
|
||||
'submit'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predefiniowana akcja Anuluj
|
||||
*/
|
||||
public static function cancel(string $backUrl, string $label = 'Anuluj'): self
|
||||
{
|
||||
return new self(
|
||||
'cancel',
|
||||
$label,
|
||||
$backUrl,
|
||||
null,
|
||||
'btn btn-default',
|
||||
'link'
|
||||
);
|
||||
}
|
||||
}
|
||||
178
autoload/admin/ViewModels/Forms/FormEditViewModel.php
Normal file
178
autoload/admin/ViewModels/Forms/FormEditViewModel.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Forms;
|
||||
|
||||
/**
|
||||
* Główny model widoku formularza edycji
|
||||
*/
|
||||
class FormEditViewModel
|
||||
{
|
||||
public string $formId;
|
||||
public string $title;
|
||||
public string $method;
|
||||
public string $action;
|
||||
public ?string $backUrl;
|
||||
public array $tabs;
|
||||
public array $fields;
|
||||
public array $hiddenFields;
|
||||
public array $actions;
|
||||
public bool $persist;
|
||||
public array $data;
|
||||
public ?array $validationErrors;
|
||||
public ?array $languages;
|
||||
|
||||
/**
|
||||
* @param string $formId Unikalny identyfikator formularza
|
||||
* @param string $title Tytuł formularza
|
||||
* @param array $data Dane obiektu (np. banner)
|
||||
* @param array $fields Pola formularza
|
||||
* @param array $tabs Zakładki formularza
|
||||
* @param array $actions Akcje (przyciski)
|
||||
* @param string $method Metoda HTTP (POST, GET)
|
||||
* @param string $action URL akcji formularza
|
||||
* @param string|null $backUrl URL powrotu
|
||||
* @param bool $persist Czy zapamiętywać dane w sesji
|
||||
* @param array $hiddenFields Dodatkowe ukryte pola
|
||||
* @param array|null $languages Dostępne języki (dla sekcji językowych)
|
||||
* @param array|null $validationErrors Błędy walidacji
|
||||
*/
|
||||
public function __construct(
|
||||
string $formId,
|
||||
string $title,
|
||||
array $data = [],
|
||||
array $fields = [],
|
||||
array $tabs = [],
|
||||
array $actions = [],
|
||||
string $method = 'POST',
|
||||
string $action = '',
|
||||
?string $backUrl = null,
|
||||
bool $persist = true,
|
||||
array $hiddenFields = [],
|
||||
?array $languages = null,
|
||||
?array $validationErrors = null
|
||||
) {
|
||||
$this->formId = $formId;
|
||||
$this->title = $title;
|
||||
$this->data = $data;
|
||||
$this->fields = $fields;
|
||||
$this->tabs = $tabs;
|
||||
$this->actions = $actions;
|
||||
$this->method = $method;
|
||||
$this->action = $action;
|
||||
$this->backUrl = $backUrl;
|
||||
$this->persist = $persist;
|
||||
$this->hiddenFields = $hiddenFields;
|
||||
$this->languages = $languages;
|
||||
$this->validationErrors = $validationErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy formularz ma zakładki
|
||||
*/
|
||||
public function hasTabs(): bool
|
||||
{
|
||||
return count($this->tabs) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy formularz ma sekcje językowe
|
||||
*/
|
||||
public function hasLangSections(): bool
|
||||
{
|
||||
foreach ($this->fields as $field) {
|
||||
if ($field->type === FormFieldType::LANG_SECTION) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca pola dla konkretnej zakładki
|
||||
*/
|
||||
public function getFieldsForTab(string $tabId): array
|
||||
{
|
||||
return array_filter($this->fields, function (FormField $field) use ($tabId) {
|
||||
return $field->tabId === $tabId && $field->type !== FormFieldType::LANG_SECTION;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca sekcje językowe dla konkretnej zakładki
|
||||
*/
|
||||
public function getLangSectionsForTab(string $tabId): array
|
||||
{
|
||||
return array_filter($this->fields, function (FormField $field) use ($tabId) {
|
||||
return $field->type === FormFieldType::LANG_SECTION &&
|
||||
$field->langSectionParentTab === $tabId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera wartość pola z danych lub sesji (persist)
|
||||
*/
|
||||
public function getFieldValue(FormField $field, $languageId = null, ?string $langFieldName = null)
|
||||
{
|
||||
$fieldName = $field->name;
|
||||
|
||||
// Dla sekcji językowych - pobierz wartość z data[lang_id][field_name]
|
||||
if ($languageId !== null && $langFieldName !== null) {
|
||||
$fieldName = $langFieldName;
|
||||
return $this->data['languages'][$languageId][$fieldName] ?? null;
|
||||
}
|
||||
|
||||
// Zwykłe pole - najpierw sprawdź sesję (persist), potem dane
|
||||
if ($this->persist && isset($_SESSION['form_persist'][$this->formId][$fieldName])) {
|
||||
return $_SESSION['form_persist'][$this->formId][$fieldName];
|
||||
}
|
||||
|
||||
return $this->data[$fieldName] ?? $field->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sprawdza czy pole ma błąd walidacji
|
||||
*/
|
||||
public function hasError(string $fieldName, $languageId = null): bool
|
||||
{
|
||||
if ($this->validationErrors === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($languageId !== null) {
|
||||
return isset($this->validationErrors[$fieldName][$languageId]);
|
||||
}
|
||||
|
||||
return isset($this->validationErrors[$fieldName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera komunikat błędu dla pola
|
||||
*/
|
||||
public function getError(string $fieldName, $languageId = null): ?string
|
||||
{
|
||||
if ($languageId !== null) {
|
||||
return $this->validationErrors[$fieldName][$languageId] ?? null;
|
||||
}
|
||||
return $this->validationErrors[$fieldName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Czyści dane persist z sesji
|
||||
*/
|
||||
public function clearPersist(): void
|
||||
{
|
||||
if (isset($_SESSION['form_persist'][$this->formId])) {
|
||||
unset($_SESSION['form_persist'][$this->formId]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zapisuje dane do sesji (persist)
|
||||
*/
|
||||
public function saveToPersist(array $data): void
|
||||
{
|
||||
if (!isset($_SESSION['form_persist'])) {
|
||||
$_SESSION['form_persist'] = [];
|
||||
}
|
||||
$_SESSION['form_persist'][$this->formId] = $data;
|
||||
}
|
||||
}
|
||||
364
autoload/admin/ViewModels/Forms/FormField.php
Normal file
364
autoload/admin/ViewModels/Forms/FormField.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Forms;
|
||||
|
||||
/**
|
||||
* Definicja pojedynczego pola formularza
|
||||
*/
|
||||
class FormField
|
||||
{
|
||||
public string $name;
|
||||
public string $type;
|
||||
public string $label;
|
||||
public $value;
|
||||
public string $tabId;
|
||||
public bool $required;
|
||||
public array $attributes;
|
||||
public array $options;
|
||||
public ?string $helpText;
|
||||
public ?string $placeholder;
|
||||
public ?string $id;
|
||||
|
||||
// Specyficzne dla obrazów/plików
|
||||
public bool $useFilemanager;
|
||||
public ?string $filemanagerUrl;
|
||||
|
||||
// Specyficzne dla edytora
|
||||
public string $editorToolbar;
|
||||
public int $editorHeight;
|
||||
|
||||
// Specyficzne dla lang_section
|
||||
public ?array $langFields;
|
||||
public ?string $langSectionParentTab;
|
||||
public ?string $customHtml;
|
||||
|
||||
/**
|
||||
* @param string $name Nazwa pola (name)
|
||||
* @param string $type Typ pola (z FormFieldType)
|
||||
* @param string $label Etykieta pola
|
||||
* @param mixed $value Wartość domyślna
|
||||
* @param string $tabId Identyfikator zakładki
|
||||
* @param bool $required Czy pole wymagane
|
||||
* @param array $attributes Atrybuty HTML
|
||||
* @param array $options Opcje dla select
|
||||
* @param string|null $helpText Tekst pomocniczy
|
||||
* @param string|null $placeholder Placeholder
|
||||
* @param bool $useFilemanager Czy używać filemanagera
|
||||
* @param string|null $filemanagerUrl URL filemanagera
|
||||
* @param string $editorToolbar Konfiguracja toolbar CKEditor
|
||||
* @param int $editorHeight Wysokość edytora
|
||||
* @param array|null $langFields Pola w sekcji językowej
|
||||
* @param string|null $langSectionParentTab Zakładka nadrzędna dla sekcji językowej
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $type = FormFieldType::TEXT,
|
||||
string $label = '',
|
||||
$value = null,
|
||||
string $tabId = 'default',
|
||||
bool $required = false,
|
||||
array $attributes = [],
|
||||
array $options = [],
|
||||
?string $helpText = null,
|
||||
?string $placeholder = null,
|
||||
bool $useFilemanager = false,
|
||||
?string $filemanagerUrl = null,
|
||||
string $editorToolbar = 'MyTool',
|
||||
int $editorHeight = 300,
|
||||
?array $langFields = null,
|
||||
?string $langSectionParentTab = null,
|
||||
?string $customHtml = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->label = $label;
|
||||
$this->value = $value;
|
||||
$this->tabId = $tabId;
|
||||
$this->required = $required;
|
||||
$this->attributes = $attributes;
|
||||
$this->options = $options;
|
||||
$this->helpText = $helpText;
|
||||
$this->placeholder = $placeholder;
|
||||
$this->useFilemanager = $useFilemanager;
|
||||
$this->filemanagerUrl = $filemanagerUrl;
|
||||
$this->editorToolbar = $editorToolbar;
|
||||
$this->editorHeight = $editorHeight;
|
||||
$this->langFields = $langFields;
|
||||
$this->langSectionParentTab = $langSectionParentTab;
|
||||
$this->customHtml = $customHtml;
|
||||
$this->id = $attributes['id'] ?? $name;
|
||||
}
|
||||
|
||||
// Factory methods dla różnych typów pól
|
||||
|
||||
public static function text(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::TEXT,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
$config['help'] ?? null,
|
||||
$config['placeholder'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public static function number(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::NUMBER,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
$config['help'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public static function email(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::EMAIL,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
public static function password(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::PASSWORD,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
public static function date(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::DATE,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
array_merge(['class' => 'date'], $config['attributes'] ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
public static function datetime(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::DATETIME,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
array_merge(['class' => 'datetime'], $config['attributes'] ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
public static function switch(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::SWITCH,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? false,
|
||||
$config['tab'] ?? 'default',
|
||||
false,
|
||||
$config['attributes'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
public static function select(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::SELECT,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
$config['options'] ?? []
|
||||
);
|
||||
}
|
||||
|
||||
public static function textarea(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::TEXTAREA,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
array_merge(['rows' => $config['rows'] ?? 4], $config['attributes'] ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
public static function editor(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::EDITOR,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
$config['toolbar'] ?? 'MyTool',
|
||||
$config['height'] ?? 300
|
||||
);
|
||||
}
|
||||
|
||||
public static function image(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::IMAGE,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
$config['filemanager'] ?? true,
|
||||
$config['filemanager_url'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public static function file(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::FILE,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
$config['filemanager'] ?? true
|
||||
);
|
||||
}
|
||||
|
||||
public static function color(string $name, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::COLOR,
|
||||
$config['label'] ?? '',
|
||||
$config['value'] ?? null,
|
||||
$config['tab'] ?? 'default',
|
||||
$config['required'] ?? false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
$config['help'] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
public static function hidden(string $name, $value = null): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::HIDDEN,
|
||||
'',
|
||||
$value,
|
||||
'default'
|
||||
);
|
||||
}
|
||||
|
||||
public static function custom(string $name, string $html, array $config = []): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::CUSTOM,
|
||||
$config['label'] ?? '',
|
||||
null,
|
||||
$config['tab'] ?? 'default',
|
||||
false,
|
||||
$config['attributes'] ?? [],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
'MyTool',
|
||||
300,
|
||||
null,
|
||||
null,
|
||||
$html
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sekcja językowa - grupa pól powtarzana dla każdego języka
|
||||
*
|
||||
* @param string $name Nazwa sekcji (prefiks dla pól)
|
||||
* @param string $parentTab Identyfikator zakładki nadrzędnej
|
||||
* @param array $fields Pola w sekcji językowej (tablica FormField)
|
||||
*/
|
||||
public static function langSection(string $name, string $parentTab, array $fields): self
|
||||
{
|
||||
return new self(
|
||||
$name,
|
||||
FormFieldType::LANG_SECTION,
|
||||
'',
|
||||
null,
|
||||
$parentTab,
|
||||
false,
|
||||
[],
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
'MyTool',
|
||||
300,
|
||||
$fields,
|
||||
$parentTab
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca nazwę pola z sufiksem dla konkretnego języka
|
||||
*/
|
||||
public function getLocalizedName($languageId): string
|
||||
{
|
||||
return "{$this->name}[{$languageId}]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca ID pola z sufiksem dla konkretnego języka
|
||||
*/
|
||||
public function getLocalizedId($languageId): string
|
||||
{
|
||||
return "{$this->id}_{$languageId}";
|
||||
}
|
||||
}
|
||||
25
autoload/admin/ViewModels/Forms/FormFieldType.php
Normal file
25
autoload/admin/ViewModels/Forms/FormFieldType.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Forms;
|
||||
|
||||
/**
|
||||
* Dostępne typy pól formularza
|
||||
*/
|
||||
class FormFieldType
|
||||
{
|
||||
public const TEXT = 'text';
|
||||
public const NUMBER = 'number';
|
||||
public const EMAIL = 'email';
|
||||
public const PASSWORD = 'password';
|
||||
public const DATE = 'date';
|
||||
public const DATETIME = 'datetime';
|
||||
public const SWITCH = 'switch';
|
||||
public const SELECT = 'select';
|
||||
public const TEXTAREA = 'textarea';
|
||||
public const EDITOR = 'editor';
|
||||
public const IMAGE = 'image';
|
||||
public const FILE = 'file';
|
||||
public const HIDDEN = 'hidden';
|
||||
public const LANG_SECTION = 'lang_section';
|
||||
public const CUSTOM = 'custom';
|
||||
public const COLOR = 'color';
|
||||
}
|
||||
31
autoload/admin/ViewModels/Forms/FormTab.php
Normal file
31
autoload/admin/ViewModels/Forms/FormTab.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace admin\ViewModels\Forms;
|
||||
|
||||
/**
|
||||
* Definicja zakładki formularza
|
||||
*/
|
||||
class FormTab
|
||||
{
|
||||
public string $id;
|
||||
public string $label;
|
||||
public string $icon;
|
||||
public ?string $parentTabId;
|
||||
|
||||
/**
|
||||
* @param string $id Unikalny identyfikator zakładki
|
||||
* @param string $label Etykieta wyświetlana
|
||||
* @param string $icon Klasa FontAwesome (np. 'fa-wrench')
|
||||
* @param string|null $parentTabId Identyfikator zakładki nadrzędnej (dla zagnieżdżenia)
|
||||
*/
|
||||
public function __construct(
|
||||
string $id,
|
||||
string $label,
|
||||
string $icon = '',
|
||||
?string $parentTabId = null
|
||||
) {
|
||||
$this->id = $id;
|
||||
$this->label = $label;
|
||||
$this->icon = $icon;
|
||||
$this->parentTabId = $parentTabId;
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
<?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()
|
||||
{
|
||||
$sa = \S::get('s-action');
|
||||
$domain = preg_replace('/^www\./', '', $_SERVER['SERVER_NAME']);
|
||||
$cookie_name = 'admin_remember_' . str_replace( '.', '-', $domain );
|
||||
|
||||
switch ($sa)
|
||||
{
|
||||
case 'user-logon':
|
||||
{
|
||||
$login = \S::get('login');
|
||||
$pass = \S::get('password');
|
||||
|
||||
$result = \admin\factory\Users::logon($login, $pass);
|
||||
|
||||
if ( $result == 1 )
|
||||
{
|
||||
$user = \admin\factory\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 ( !\admin\factory\Users::send_twofa_code( (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 = \admin\factory\Users::details($login);
|
||||
|
||||
self::finalize_admin_login(
|
||||
$user,
|
||||
$domain,
|
||||
$cookie_name,
|
||||
(bool)\S::get('remember')
|
||||
);
|
||||
|
||||
header('Location: /admin/articles/view_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 = \admin\factory\Users::verify_twofa_code((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 = \admin\factory\Users::details($pending['login']);
|
||||
|
||||
self::finalize_admin_login(
|
||||
$user,
|
||||
$domain,
|
||||
$cookie_name,
|
||||
$pending['remember'] ? true : false
|
||||
);
|
||||
|
||||
header('Location: /admin/articles/view_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 (!\admin\factory\Users::send_twofa_code((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;
|
||||
}
|
||||
}
|
||||
|
||||
public static function route()
|
||||
{
|
||||
$_SESSION['admin'] = true;
|
||||
|
||||
if ( \S::get( 'p' ) )
|
||||
\S::set_session( 'p' , \S::get( 'p' ) );
|
||||
|
||||
$page = \S::get_session( 'p' );
|
||||
|
||||
$class = '\admin\controls\\';
|
||||
|
||||
$results = explode( '_', \S::get( 'module' ) );
|
||||
if ( is_array( $results ) ) foreach ( $results as $row )
|
||||
$class .= ucfirst( $row );
|
||||
|
||||
$action = \S::get( 'action' );
|
||||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
class Archive {
|
||||
static public function products_list() {
|
||||
|
||||
$current_page = \S::get_session( 'archive_products_list_current_page' );
|
||||
|
||||
if ( !$current_page ) {
|
||||
$current_page = 1;
|
||||
\S::set_session( 'archive_products_list_current_page', $current_page );
|
||||
}
|
||||
|
||||
$query = \S::get_session( 'archive_products_list_query' );
|
||||
if ( $query ) {
|
||||
$query_array = [];
|
||||
parse_str( $query, $query_array );
|
||||
}
|
||||
|
||||
return \Tpl::view( 'archive/products-list', [
|
||||
'current_page' => $current_page,
|
||||
'query_array' => $query_array,
|
||||
'pagination_max' => ceil( \admin\factory\ShopProduct::count_product() / 10 )
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Articles
|
||||
{
|
||||
public static function gallery_order_save()
|
||||
{
|
||||
if ( \admin\factory\Articles::gallery_order_save( \S::get( 'article_id' ), \S::get( 'order' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok', 'msg' => 'Artykuł został zapisany.' ] );
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function browse_list()
|
||||
{
|
||||
return \admin\view\Articles::browse_list();
|
||||
}
|
||||
|
||||
public static function article_delete()
|
||||
{
|
||||
if ( \admin\factory\Articles::articles_set_archive( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Artykuł został przeniesiony do archiwum.' );
|
||||
header( 'Location: /admin/articles/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function article_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania artykułu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Articles::article_save(
|
||||
$values['id'], $values['title'], $values['main_image'], $values['entry'], $values['text'], $values['table_of_contents'], $values['status'], $values['show_title'], $values['show_table_of_contents'], $values['show_date_add'], $values['date_add'], $values['show_date_modify'], $values['date_modify'], $values['seo_link'], $values['meta_title'],
|
||||
$values['meta_description'], $values['meta_keywords'], $values['layout_id'], $values['pages'], $values['noindex'], $values['repeat_entry'], $values['copy_from'], $values['social_icons'], $values['block_direct_access']
|
||||
) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Artykuł został zapisany.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function article_edit() {
|
||||
global $user;
|
||||
|
||||
if ( !$user ) {
|
||||
header( 'Location: /admin/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
\admin\factory\Articles::delete_nonassigned_images();
|
||||
\admin\factory\Articles::delete_nonassigned_files();
|
||||
|
||||
return \Tpl::view( 'articles/article-edit', [
|
||||
'article' => \admin\factory\Articles::article_details( (int)\S::get( 'id' ) ),
|
||||
'menus' => \admin\factory\Pages::menus_list(),
|
||||
'languages' => \admin\factory\Languages::languages_list(),
|
||||
'layouts' => \admin\factory\Layouts::layouts_list(),
|
||||
'user' => $user
|
||||
] );
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\Articles::articles_list();
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class ArticlesArchive
|
||||
{
|
||||
public static function article_restore()
|
||||
{
|
||||
if ( \admin\factory\ArticlesArchive::article_restore( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Artykuł został przywrócony.' );
|
||||
header( 'Location: /admin/articles_archive/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function article_delete()
|
||||
{
|
||||
if ( \admin\factory\ArticlesArchive::article_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Artykuł został usunięty.' );
|
||||
header( 'Location: /admin/articles_archive/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\ArticlesArchive::articles_list();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace admin\controls;
|
||||
|
||||
class Banners
|
||||
{
|
||||
public static function banner_delete()
|
||||
{
|
||||
if ( \admin\factory\Banners::banner_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Baner został usunięty.' );
|
||||
header( 'Location: /admin/banners/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function banner_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania baneru wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $banner_id = \admin\factory\Banners::banner_save( $values['id'], $values['name'], $values['status'], $values['date_start'], $values['date_end'],
|
||||
$values['home_page'], $values['src'], $values['url'], $values['html'], $values['text'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Baner został zapisany.', 'id' => $banner_id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function banner_edit()
|
||||
{
|
||||
return \admin\view\Banners::banner_edit(
|
||||
\admin\factory\Banners::banner_details(
|
||||
\S::get( 'id' )
|
||||
),
|
||||
\admin\factory\Languages::languages_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\Banners::banners_list();
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Baselinker {
|
||||
|
||||
// widok wiązania produktów
|
||||
static public function bundling_products() {
|
||||
return \Tpl::view( 'baselinker/bundling-products', [
|
||||
'products' => \admin\factory\ShopProduct::products_list_for_baselinker(),
|
||||
'baselinker_products' => \admin\factory\Baselinker::products_list()
|
||||
] );
|
||||
}
|
||||
|
||||
// zapis wiązania produktów
|
||||
static public function bundling_products_save() {
|
||||
\admin\factory\Baselinker::bundling_products_save( $_POST );
|
||||
header( 'Location: /admin/baselinker/bundling_products/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
class Dashboard
|
||||
{
|
||||
static public function main_view()
|
||||
{
|
||||
return \Tpl::view( 'dashboard/main-view', [
|
||||
'last_orders' => \shop\Dashboard::last_orders(),
|
||||
'order_statuses' => \shop\Order::order_statuses(),
|
||||
'sales' => \shop\Dashboard::last_24_months_sales(),
|
||||
'best_sales_products' => \shop\Dashboard::best_sales_products(),
|
||||
'most_view_products' => \shop\Dashboard::most_view_products(),
|
||||
'sales_grid' => \shop\Dashboard::sales_grid(),
|
||||
'summary_sales' => \shop\Dashboard::summary_sales(),
|
||||
'summary_orders' => \shop\Dashboard::summary_orders(),
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Dictionaries {
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'dictionaries/units-list' );
|
||||
}
|
||||
|
||||
public static function unit_edit()
|
||||
{
|
||||
return \Tpl::view( 'dictionaries/unit-edit', [
|
||||
'unit' => \admin\factory\Dictionaries::unit_details( \S::get( 'id' )),
|
||||
'languages' => \admin\factory\Languages::languages_list(),
|
||||
] );
|
||||
}
|
||||
|
||||
static public function unit_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania jednostki miary wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Dictionaries::unit_save( $values['id'], $values['text']) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Jednostka miary została zapisana.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
static public function unit_delete()
|
||||
{
|
||||
if ( \admin\factory\Dictionaries::unit_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Jesdnostka miary została usunięta.' );
|
||||
|
||||
header( 'Location: /admin/dictionaries/view_list/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Filemanager
|
||||
{
|
||||
static public function draw()
|
||||
{
|
||||
return \Tpl::view( 'filemanager/filemanager' );
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,846 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
|
||||
class Integrations {
|
||||
|
||||
// apilo_create_product
|
||||
static public function apilo_create_product()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$product_id = \S::get( 'product_id' );
|
||||
$product = new \shop\Product( $product_id );
|
||||
|
||||
$methodParams = [
|
||||
"sku" => $product -> sku,
|
||||
"ean" => $product -> ean,
|
||||
"name" => $product -> language['name'],
|
||||
"tax" => (int) $product -> vat,
|
||||
'status' => 1,
|
||||
"quantity" => (int) $product -> quantity,
|
||||
"priceWithTax" => $product -> price_brutto,
|
||||
'description' => $product -> language['description'] . '<br>' . $product -> language['short_description'],
|
||||
'shortDescription' => '',
|
||||
'images' => []
|
||||
];
|
||||
|
||||
foreach ($product->images as $image) {
|
||||
$methodParams["images"][] = "https://" . $_SERVER['HTTP_HOST'] . $image['src'];
|
||||
}
|
||||
|
||||
$ch = curl_init( "https://projectpro.apilo.com/rest/api/warehouse/product/" );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( [ $methodParams ] ) );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Content-Type: application/json",
|
||||
"Accept: application/json"
|
||||
] );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData['products'] )
|
||||
{
|
||||
$mdb -> update( 'pp_shop_products', [ 'apilo_product_id' => reset( $responseData['products'] ), 'apilo_product_name' => $product->language['name'] ], [ 'id' => $product -> id ] );
|
||||
}
|
||||
|
||||
\S::alert( 'Produkt został dodany do magazynu APILO.' );
|
||||
}
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// baselinker_create_product
|
||||
static public function baselinker_create_product()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$api_code = \admin\factory\Integrations::baselinker_settings( 'api_code' );
|
||||
|
||||
$product_id = \S::get( 'product_id' );
|
||||
$product = \shop\Product::getFromCache( $product_id, 'pl' );
|
||||
|
||||
$methodParams = [
|
||||
"storage_id" => \admin\factory\Integrations::baselinker_settings('storage_id'),
|
||||
"ean" => $product->ean,
|
||||
"sku" => $product->sku,
|
||||
"name" => $product->language['name'],
|
||||
"quantity" => "0",
|
||||
"price_brutto" => $product->price_brutto,
|
||||
"tax_rate" => $product->vat,
|
||||
"description" => $product->language['short_description'],
|
||||
"description_extra1" => $product->language['description'],
|
||||
"images" => []
|
||||
];
|
||||
|
||||
foreach ($product->images as $image) {
|
||||
$methodParams["images"][] = "url:https://" . $_SERVER['HTTP_HOST'] . $image['src'];
|
||||
}
|
||||
|
||||
$methodParams = json_encode( $methodParams, JSON_UNESCAPED_SLASHES );
|
||||
|
||||
$apiParams = [
|
||||
"token" => $api_code,
|
||||
"method" => "addProduct",
|
||||
"parameters" => $methodParams
|
||||
];
|
||||
|
||||
$curl = curl_init( "https://api.baselinker.com/connector.php" );
|
||||
curl_setopt( $curl, CURLOPT_POST, 1 );
|
||||
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
|
||||
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = json_decode( curl_exec( $curl ), true );
|
||||
|
||||
if ( $response['status'] == 'SUCCESS' )
|
||||
{
|
||||
if ( $response['product_id'] )
|
||||
{
|
||||
$mdb -> update( 'pp_shop_products', [ 'baselinker_product_id' => $response['product_id'], 'baselinker_product_name' => $product->language['name'] ], [ 'id' => $product -> id ] );
|
||||
}
|
||||
|
||||
\S::alert( 'Produkt został dodany do magazynu Baselinker.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Podczas dodawania produktu wystąpił błąd.' );
|
||||
}
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// baselinker pobierz listę magazynów
|
||||
static public function baselinker_get_storages_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$api_code = \admin\factory\Integrations::baselinker_settings( 'api_code' );
|
||||
|
||||
$methodParams = '{
|
||||
"storage_id": "bl_1"
|
||||
}';
|
||||
|
||||
$apiParams = [
|
||||
"token" => $api_code,
|
||||
"method" => "getStoragesList",
|
||||
"parameters" => $methodParams
|
||||
];
|
||||
|
||||
$curl = curl_init( "https://api.baselinker.com/connector.php" );
|
||||
curl_setopt( $curl, CURLOPT_POST, 1 );
|
||||
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
|
||||
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = json_decode( curl_exec( $curl ), true );
|
||||
|
||||
if ( $response['status'] == 'SUCCESS' )
|
||||
{
|
||||
\admin\factory\Integrations::baselinker_settings_save( 'storages_list', $response['storages'] );
|
||||
\S::alert( 'Lista magazynów została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
header( 'Location: /admin/integrations/baselinker_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// baselinker_get_order_status_list
|
||||
static public function baselinker_get_order_status_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$api_code = \admin\factory\Integrations::baselinker_settings( 'api_code' );
|
||||
|
||||
$apiParams = [
|
||||
"token" => $api_code,
|
||||
"method" => "getOrderStatusList",
|
||||
"parameters" => []
|
||||
];
|
||||
|
||||
$curl = curl_init( "https://api.baselinker.com/connector.php" );
|
||||
curl_setopt( $curl, CURLOPT_POST, 1 );
|
||||
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
|
||||
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = json_decode( curl_exec( $curl ), true );
|
||||
|
||||
if ( $response['status'] == 'SUCCESS' )
|
||||
{
|
||||
\admin\factory\Integrations::baselinker_settings_save( 'order_status_list', $response['statuses'] );
|
||||
\S::alert( 'Lista statusów została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
header( 'Location: /admin/integrations/baselinker_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_sellasist_carriers_list
|
||||
static public function get_sellasist_shipments_list()
|
||||
{
|
||||
$api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );
|
||||
|
||||
$ch = curl_init( "https://projectpro.sellasist.pl/api/v1/shipments" );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"apiKey: " . $api_code,
|
||||
"accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
else {
|
||||
if ( $responseData ) {
|
||||
\admin\factory\Integrations::sellasist_settings_save( 'shipments_methods', $responseData );
|
||||
\S::alert( 'Lista przewoźników została pobrana.' );
|
||||
} else
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
header( 'Location: /admin/integrations/sellasist_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_sellasist_payment_types_list
|
||||
static public function get_sellasist_payment_types_list()
|
||||
{
|
||||
$api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );
|
||||
|
||||
$ch = curl_init( "https://projectpro.sellasist.pl/api/v1/payments" );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"apiKey: " . $api_code,
|
||||
"accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
else {
|
||||
if ( $responseData ) {
|
||||
\admin\factory\Integrations::sellasist_settings_save( 'payment_types_list', $responseData );
|
||||
\S::alert( 'Lista metod płatności została pobrana.' );
|
||||
} else
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
header( 'Location: /admin/integrations/sellasist_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function get_sellasist_status_types_list()
|
||||
{
|
||||
$api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );
|
||||
|
||||
$ch = curl_init( "https://projectpro.sellasist.pl/api/v1/statuses" );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"apiKey: " . $api_code,
|
||||
"accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
else {
|
||||
if ( $responseData ) {
|
||||
\admin\factory\Integrations::sellasist_settings_save( 'status_types_list', $responseData );
|
||||
\S::alert( 'Lista statusów została pobrana.' );
|
||||
} else
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
header( 'Location: /admin/integrations/sellasist_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_platform_list
|
||||
static public function get_platform_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/orders/platform/map/";
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData )
|
||||
{
|
||||
\admin\factory\Integrations::apilo_settings_save( 'platform-list', $responseData );
|
||||
\S::alert( 'Lista platform została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
}
|
||||
header( 'Location: /admin/integrations/apilo_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_status_types_list
|
||||
static public function get_status_types_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/orders/status/map/";
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData )
|
||||
{
|
||||
\admin\factory\Integrations::apilo_settings_save( 'status-types-list', $responseData );
|
||||
\S::alert( 'Lista statusów została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
}
|
||||
header( 'Location: /admin/integrations/apilo_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_carrier_account_list
|
||||
static public function get_carrier_account_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/orders/carrier-account/map/";
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData )
|
||||
{
|
||||
\admin\factory\Integrations::apilo_settings_save( 'carrier-account-list', $responseData );
|
||||
\S::alert( 'Lista kont przewoźników została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
}
|
||||
header( 'Location: /admin/integrations/apilo_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_payment_types_list
|
||||
static public function get_payment_types_list()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/orders/payment/map/";
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
\S::alert( 'Błąd cURL: ' . curl_error( $ch ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData )
|
||||
{
|
||||
\admin\factory\Integrations::apilo_settings_save( 'payment-types-list', $responseData );
|
||||
\S::alert( 'Lista metod płatności została pobrana.' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\S::alert( 'Brak wyników.' );
|
||||
}
|
||||
}
|
||||
header( 'Location: /admin/integrations/apilo_settings/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// settings for the sellasist integration
|
||||
static public function sellasist_settings()
|
||||
{
|
||||
return \Tpl::view( 'integrations/sellasist-settings', [
|
||||
'settings' => \admin\factory\Integrations::sellasist_settings(),
|
||||
] );
|
||||
}
|
||||
|
||||
// settings for the Baselinker integration
|
||||
static public function baselinker_settings()
|
||||
{
|
||||
return \Tpl::view( 'integrations/baselinker-settings', [
|
||||
'settings' => \admin\factory\Integrations::baselinker_settings(),
|
||||
] );
|
||||
}
|
||||
|
||||
// settings for shoppro
|
||||
static public function shoppro_settings()
|
||||
{
|
||||
return \Tpl::view( 'integrations/shoppro-settings', [
|
||||
'settings' => \admin\factory\Integrations::shoppro_settings(),
|
||||
] );
|
||||
}
|
||||
|
||||
// Settings for the APILO plugin
|
||||
static public function apilo_settings()
|
||||
{
|
||||
|
||||
return \Tpl::view( 'integrations/apilo-settings', [
|
||||
'settings' => \admin\factory\Integrations::apilo_settings(),
|
||||
] );
|
||||
}
|
||||
|
||||
// save settings for the shoppro integration
|
||||
static public function shoppro_settings_save()
|
||||
{
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawień wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$field_id = \S::get( 'field_id' );
|
||||
$value = \S::get( 'value' );
|
||||
|
||||
if ( \admin\factory\Integrations::shoppro_settings_save( $field_id, $value ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostały zapisane.', 'value' => $value ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// save settings for the sellasist integration
|
||||
static public function sellasist_settings_save()
|
||||
{
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawień wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$field_id = \S::get( 'field_id' );
|
||||
$value = \S::get( 'value' );
|
||||
|
||||
if ( \admin\factory\Integrations::sellasist_settings_save( $field_id, $value ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostały zapisane.', 'value' => $value ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// save settings for the Baselinker integration
|
||||
static public function baselinker_settings_save()
|
||||
{
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawień wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$field_id = \S::get( 'field_id' );
|
||||
$value = \S::get( 'value' );
|
||||
|
||||
if ( \admin\factory\Integrations::baselinker_settings_save( $field_id, $value ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostały zapisane.', 'value' => $value ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// Save settings for the APILO plugin
|
||||
static public function apilo_settings_save() {
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania ustawień wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$field_id = \S::get( 'field_id' );
|
||||
$value = \S::get( 'value' );
|
||||
|
||||
if ( \admin\factory\Integrations::apilo_settings_save( $field_id, $value ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Ustawienia zostały zapisane.', 'value' => $value ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// Authorization in apilo.com
|
||||
static public function apilo_authorization() {
|
||||
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas autoryzacji wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$settings = \admin\factory\Integrations::apilo_settings();
|
||||
|
||||
if ( \admin\factory\Integrations::apilo_authorization( $settings['client-id'], $settings['client-secret'], $settings['authorization-code'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Autoryzacja przebiegła pomyślnie.' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// sellasist product search by sku
|
||||
static public function sellasist_product_search() {
|
||||
global $mdb, $settings;
|
||||
|
||||
$sku = $mdb -> get( 'pp_shop_products', 'sku', [ 'id' => \S::get( 'product_id' ) ] );
|
||||
|
||||
if ( !$sku ) {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podany produkt nie posiada kodu SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$url = "https://projectpro.sellasist.pl/api/v1/products";
|
||||
$params['symbol '] = $sku;
|
||||
$url .= '?' . http_build_query( $params );
|
||||
|
||||
$api_code = \admin\factory\Integrations::sellasist_settings( 'api_code' );
|
||||
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"apiKey: " . $api_code,
|
||||
"accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) )
|
||||
{
|
||||
echo 'Błąd cURL: ' . curl_error( $ch );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $responseData['error'] )
|
||||
{
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Brak wyników dla podanego SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
$return_data = array();
|
||||
$return_data['status'] = 'SUCCESS';
|
||||
$return_data['products'] = $responseData;
|
||||
echo json_encode( $return_data );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Brak wyników dla podanego SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// apilo product search by sku
|
||||
static public function apilo_product_search() {
|
||||
global $mdb, $settings;
|
||||
|
||||
$sku = $mdb -> get( 'pp_shop_products', 'sku', [ 'id' => \S::get( 'product_id' ) ] );
|
||||
|
||||
if ( !$sku ) {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podany produkt nie posiada kodu SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$access_token = \admin\factory\Integrations::apilo_get_access_token();
|
||||
|
||||
$url = "https://projectpro.apilo.com/rest/api/warehouse/product/";
|
||||
$params['sku'] = $sku;
|
||||
$url .= '?' . http_build_query( $params );
|
||||
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer " . $access_token,
|
||||
"Accept: application/json"
|
||||
] );
|
||||
|
||||
$response = curl_exec( $ch );
|
||||
$responseData = json_decode( $response, true );
|
||||
|
||||
if ( curl_errno( $ch ) ) {
|
||||
echo 'Błąd cURL: ' . curl_error( $ch );
|
||||
} else {
|
||||
if ( $responseData && isset( $responseData['products'] ) ) {
|
||||
$responseData['status'] = 'SUCCESS';
|
||||
echo json_encode( $responseData );
|
||||
exit;
|
||||
} else {
|
||||
echo json_encode( [ 'status' => 'SUCCESS', 'msg' => 'Brak wyników dla podanego SKU.', 'products' => '' ] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
echo json_encode( [ 'status' => 'SUCCESS', 'msg' => 'Brak wyników dla podanego SKU.', 'products' => '' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// wyszukiwanie produktu w bazie baselinkera
|
||||
static public function baselinker_product_search()
|
||||
{
|
||||
global $mdb, $settings;
|
||||
|
||||
$api_code = \admin\factory\Integrations::baselinker_settings( 'api_code' );
|
||||
|
||||
$sku = $mdb -> get( 'pp_shop_products', 'sku', [ 'id' => \S::get( 'product_id' ) ] );
|
||||
|
||||
if ( !$sku ) {
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podany produkt nie posiada kodu SKU.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
$methodParams = '{
|
||||
"storage_id": "bl_1",
|
||||
"filter_sku": "' . $sku . '"
|
||||
}';
|
||||
|
||||
$apiParams = [
|
||||
"token" => $api_code,
|
||||
"method" => "getProductsList",
|
||||
"parameters" => $methodParams
|
||||
];
|
||||
|
||||
$curl = curl_init( "https://api.baselinker.com/connector.php" );
|
||||
curl_setopt( $curl, CURLOPT_POST, 1 );
|
||||
curl_setopt( $curl, CURLOPT_POSTFIELDS, http_build_query( $apiParams ) );
|
||||
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = json_decode( curl_exec( $curl ), true );
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
// sellasist_product_select_delete
|
||||
static public function sellasist_product_select_delete()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::sellasist_product_select_delete( \S::get( 'product_id' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas usuwania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// apilo product select delete
|
||||
static public function apilo_product_select_delete()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::apilo_product_select_delete( \S::get( 'product_id' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas usuwania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// baselinker delete product linking
|
||||
static public function baselinker_product_select_delete()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::baselinker_product_select_delete( \S::get( 'product_id' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas usuwania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// sellasist_product_select_save
|
||||
static public function sellasist_product_select_save()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::sellasist_product_select_save( \S::get( 'product_id' ), \S::get( 'sellasist_product_id' ), \S::get( 'sellasist_product_name' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas zapisywania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// apilo product select save
|
||||
static public function apilo_product_select_save()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::apilo_product_select_save( \S::get( 'product_id' ), \S::get( 'apilo_product_id' ), \S::get( 'apilo_product_name' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas zapisywania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function baselinker_product_select_save() {
|
||||
global $mdb;
|
||||
|
||||
if ( \admin\factory\Integrations::baselinker_product_select_save( \S::get( 'product_id' ), \S::get( 'baselinker_product_id' ), \S::get( 'baselinker_product_name' ) ) )
|
||||
echo json_encode( [ 'status' => 'ok' ] );
|
||||
else
|
||||
echo json_encode( [ 'status' => 'error', 'msg' => 'Podczas zapisywania produktu wystąpił błąd. Proszę spróbować ponownie.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// shoppro_product_import
|
||||
static public function shoppro_product_import()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$shoppro_settings = \admin\factory\Integrations::shoppro_settings();
|
||||
|
||||
$mdb2 = new \medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $shoppro_settings[ 'db_name' ],
|
||||
'server' => $shoppro_settings[ 'db_host' ],
|
||||
'username' => $shoppro_settings[ 'db_user' ],
|
||||
'password' => $shoppro_settings[ 'db_password' ],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$product_id = \S::get( 'product_id' );
|
||||
|
||||
$product = $mdb2 -> get( 'pp_shop_products', '*', [ 'id' => $product_id ] );
|
||||
if ( $product )
|
||||
{
|
||||
$mdb -> insert( 'pp_shop_products', [
|
||||
'price_netto' => $product[ 'price_netto' ],
|
||||
'price_brutto' => $product[ 'price_brutto' ],
|
||||
'vat' => $product[ 'vat' ],
|
||||
'stock_0_buy' => $product[ 'stock_0_buy' ],
|
||||
'quantity' => $product[ 'quantity' ],
|
||||
'wp' => $product[ 'wp' ],
|
||||
'sku' => $product[ 'sku' ],
|
||||
'ean' => $product[ 'ean' ],
|
||||
'custom_label_0' => $product[ 'custom_label_0' ],
|
||||
'custom_label_1' => $product[ 'custom_label_1' ],
|
||||
'custom_label_2' => $product[ 'custom_label_2' ],
|
||||
'custom_label_3' => $product[ 'custom_label_3' ],
|
||||
'custom_label_4' => $product[ 'custom_label_4' ],
|
||||
'additional_message' => $product[ 'additional_message' ],
|
||||
'additional_message_text' => $product[ 'additional_message_text' ],
|
||||
'additional_message_required' => $product[ 'additional_message_required' ],
|
||||
'weight' => $product[ 'weight' ]
|
||||
] );
|
||||
|
||||
$new_product_id = $mdb -> id();
|
||||
if ( $new_product_id )
|
||||
{
|
||||
$languages = $mdb2 -> select( 'pp_shop_products_langs', '*', [ 'product_id' => $product_id ] );
|
||||
if ( is_array( $languages ) )
|
||||
{
|
||||
foreach ( $languages as $language )
|
||||
{
|
||||
$mdb -> insert( 'pp_shop_products_langs', [
|
||||
'product_id' => $new_product_id,
|
||||
'lang_id' => $language['lang_id'],
|
||||
'name' => $language['name'],
|
||||
'short_description' => $language['short_description'],
|
||||
'description' => $language['description'],
|
||||
'tab_name_1' => $language['tab_name_1'],
|
||||
'tab_description_1' => $language['tab_description_1'],
|
||||
'tab_name_2' => $language['tab_name_2'],
|
||||
'tab_description_2' => $language['tab_description_2'],
|
||||
'meta_title' => $language['meta_title'],
|
||||
'meta_description' => $language['meta_description'],
|
||||
'meta_keywords' => $language['meta_keywords'],
|
||||
'seo_link' => $language['seo_link'],
|
||||
'copy_from' => $language['copy_from'],
|
||||
'warehouse_message_zero' => $language['warehouse_message_zero'],
|
||||
'warehouse_message_nonzero' => $language['warehouse_message_nonzero'],
|
||||
'canonical' => $language['canonical'],
|
||||
'xml_name' => $language['xml_name']
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
$images = $mdb2 -> select( 'pp_shop_products_images', '*', [ 'product_id' => $product_id ] );
|
||||
if ( is_array( $images ) )
|
||||
{
|
||||
foreach ( $images as $image )
|
||||
{
|
||||
$image_url = 'https://' . $shoppro_settings['domain'] . $image['src'];
|
||||
|
||||
// pobierz zdjęcie za pomocą curl
|
||||
$ch = curl_init( $image_url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, false );
|
||||
$image_data = curl_exec( $ch );
|
||||
$image_data;
|
||||
curl_close( $ch );
|
||||
|
||||
// ścieżdka do nowego zdjęcia to "/upload/product_images/product_[product_id]/[nazwa_zdjęcia]
|
||||
$image_name = basename( $image_url );
|
||||
$image_path = '../upload/product_images/product_' . $new_product_id . '/' . $image_name;
|
||||
|
||||
// utwórz katalog dla zdjęć produktu jeśli nie istnieje
|
||||
if ( !file_exists( '../upload/product_images/product_' . $new_product_id ) )
|
||||
mkdir( '../upload/product_images/product_' . $new_product_id, 0777, true );
|
||||
|
||||
// zapisz zdjęcie
|
||||
file_put_contents( $image_path, $image_data );
|
||||
|
||||
// zapisz zdjęcie w bazie danych
|
||||
$mdb -> insert( 'pp_shop_products_images', [
|
||||
'product_id' => $new_product_id,
|
||||
'src' => '/upload/product_images/product_' . $new_product_id . '/' . $image_name,
|
||||
'o' => $image['o']
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
\S::alert( 'Produkt został zaimportowany.' );
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
\S::alert( 'Podczas importowania produktu wystąpił błąd.' );
|
||||
header( 'Location: /admin/shop_product/view_list/' );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
|
||||
class Languages
|
||||
{
|
||||
public static function language_delete()
|
||||
{
|
||||
if ( \admin\factory\Languages::language_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Język został usunięty.' );
|
||||
header( 'Location: /admin/languages/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function language_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania języka wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( \admin\factory\Languages::language_save(
|
||||
$values['id'], $values['name'], $values['status'],
|
||||
$values['start'], $values['o'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Język został zapisany.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function language_edit()
|
||||
{
|
||||
return \admin\view\Languages::language_edit(
|
||||
\admin\factory\Languages::language_details(
|
||||
\S::get( 'id' )
|
||||
), \admin\factory\Languages::max_order()
|
||||
);
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\Languages::languages_list();
|
||||
}
|
||||
|
||||
public static function translation_delete()
|
||||
{
|
||||
if ( \admin\factory\Languages::translation_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Tłumaczenie zostało usunięte.' );
|
||||
header( 'Location: /admin/languages/translation_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function translation_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania tłumaczenia wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
$languages_list = \admin\factory\Languages::languages_list();
|
||||
if ( is_array( $languages_list ) and !empty( $languages_list ) ) foreach ( $languages_list as $language )
|
||||
{
|
||||
\S::delete_session( 'lang-' . $language['id'] );
|
||||
$languages[ $language['id'] ] = $values[ $language['id'] ];
|
||||
}
|
||||
|
||||
if ( $id = \admin\factory\Languages::translation_save( $values['id'], $values['text'], $languages ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Tłumaczenie zostało zapisane.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function translation_edit()
|
||||
{
|
||||
return \admin\view\Languages::translation_edit(
|
||||
\admin\factory\Languages::translation_details( \S::get( 'id' ) ),
|
||||
\admin\factory\Languages::languages_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function translation_list()
|
||||
{
|
||||
return \admin\view\Languages::translations_list();
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Layouts
|
||||
{
|
||||
public static function layout_delete()
|
||||
{
|
||||
if ( \admin\factory\Layouts::layout_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Szablon został usunięty.' );
|
||||
header( 'Location: /admin/layouts/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function layout_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania szablonu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Layouts::layout_save( $values['id'], $values['name'], $values['status'], $values['pages'], $values['html'], $values['css'], $values['js'], $values['m_html'],
|
||||
$values['m_css'], $values['m_js'], $values['categories'], $values['categories_default'] )
|
||||
)
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Szablon został zapisany.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function layout_edit()
|
||||
{
|
||||
return \Tpl::view( 'layouts/layout-edit', [
|
||||
'layout' => \admin\factory\Layouts::layout_details( \S::get( 'id' ) ),
|
||||
'menus' => \admin\factory\Layouts::menus_list(),
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\Layouts::layouts_list();
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Newsletter
|
||||
{
|
||||
public static function emails_list()
|
||||
{
|
||||
return \admin\view\Newsletter::emails_list();
|
||||
}
|
||||
|
||||
public static function send()
|
||||
{
|
||||
if ( \admin\factory\Newsletter::send( \S::get( 'dates' ), \S::get('template')) )
|
||||
\S::alert( 'Newsletter został dodany do kolejki wysyłania.' );
|
||||
|
||||
header( 'Location: /admin/newsletter/prepare/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function prepare()
|
||||
{
|
||||
return \admin\view\Newsletter::prepare(
|
||||
\admin\factory\Newsletter::templates_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function settings_save()
|
||||
{
|
||||
$settings = \admin\factory\Settings::settings_details();
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
\admin\factory\Settings::settings_update( 'newsletter_footer', $values['newsletter_footer'] );
|
||||
\admin\factory\Settings::settings_update( 'newsletter_header', $values['newsletter_header'] );
|
||||
|
||||
\S::alert( 'Ustawienia zostały zapisane.' );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok', 'msg' => 'Ustawienia zostały zapisane.' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function settings()
|
||||
{
|
||||
return \admin\view\Newsletter::settings(
|
||||
\admin\factory\Settings::settings_details()
|
||||
);
|
||||
}
|
||||
|
||||
public static function email_templates_user()
|
||||
{
|
||||
return \admin\view\Newsletter::email_templates_user();
|
||||
}
|
||||
|
||||
public static function email_templates_admin()
|
||||
{
|
||||
return \admin\view\Newsletter::email_templates_admin();
|
||||
}
|
||||
|
||||
public static function email_template_delete()
|
||||
{
|
||||
$is_admin = \admin\factory\Newsletter::is_admin_template( \S::get( 'id' ) );
|
||||
|
||||
if ( !$is_admin and \admin\factory\Newsletter::newsletter_template_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Szablon newslettera został usunięty.' );
|
||||
|
||||
if ( $is_admin )
|
||||
header( 'Location: /admin/newsletter/email_templates_admin/' );
|
||||
else
|
||||
header( 'Location: /admin/newsletter/email_templates_user/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function email_template_edit()
|
||||
{
|
||||
return \admin\view\Newsletter::email_template_edit(
|
||||
\admin\factory\Newsletter::email_template_detalis(
|
||||
\S::get( 'id' )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function template_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Newsletter::template_save( $values['id'], $values['name'], $values['text'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Zmiany zostały zapisane.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class Pages
|
||||
{
|
||||
public static function pages_url_browser()
|
||||
{
|
||||
echo \Tpl::view( 'pages/pages-browse-list', [
|
||||
'menus' => \admin\factory\Pages::menus_list(),
|
||||
'modal' => \S::get( 'modal' )
|
||||
] );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function browse_list()
|
||||
{
|
||||
return \admin\view\Pages::browse_list(
|
||||
\admin\factory\Pages::menus_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function menu_delete()
|
||||
{
|
||||
if ( \admin\factory\Pages::menu_delete( \S::get( 'id' ) ) )
|
||||
\S::set_message( 'Menu zostało usunięte.' );
|
||||
else
|
||||
\S::alert( 'Podczas usuwania menu wystąpił błąd. Aby usunąć menu nie może ono posiadać przypiętych stron.' );
|
||||
header( 'Location: /admin/pages/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function page_delete()
|
||||
{
|
||||
if ( \admin\factory\Pages::page_delete( \S::get( 'id' ) ) )
|
||||
\S::set_message( 'Strona została usunięta.' );
|
||||
else
|
||||
\S::alert( 'Podczas usuwania strony wystąpił błąd. Aby usunąć stronę nie może ona posiadać przypiętych podstron.' );
|
||||
header( 'Location: /admin/pages/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function page_articles()
|
||||
{
|
||||
return \admin\view\Pages::page_articles( \S::get( 'id' ), \admin\factory\Pages::page_articles( \S::get( 'id' ) ) );
|
||||
}
|
||||
|
||||
public static function page_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania strony wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Pages::page_save(
|
||||
$values['id'], $values['title'], $values['seo_link'], $values['meta_title'], $values['meta_description'], $values['meta_keywords'], $values['menu_id'], $values['parent_id'], $values['page_type'],
|
||||
$values['sort_type'], $values['layout_id'], $values['articles_limit'], $values['show_title'], $values['status'], $values['link'], $values['noindex'], $values['start'], $values['page_title'],
|
||||
$values['canonical'], $values['category_id']
|
||||
) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Strona została zapisana.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function page_edit()
|
||||
{
|
||||
return \Tpl::view( 'pages/page-edit', [
|
||||
'page' => \admin\factory\Pages::page_details( \S::get( 'id' ) ),
|
||||
'parent_id' => \S::get( 'pid' ),
|
||||
'menu_id' => \S::get( 'menu_id' ),
|
||||
'menus' => \admin\factory\Pages::menu_lists(),
|
||||
'layouts' => \admin\factory\Layouts::layouts_list(),
|
||||
'languages' => \admin\factory\Languages::languages_list()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function menu_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania menu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( \admin\factory\Pages::menu_save( $values['id'], $values['name'], $values['status'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Menu zostało zapisane.' ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function menu_edit()
|
||||
{
|
||||
return \admin\view\Pages::menu_edit(
|
||||
\admin\factory\Pages::menu_details( \S::get( 'id' ) )
|
||||
);
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'pages/pages-list', [
|
||||
'menus' => \admin\factory\Pages::menus_list()
|
||||
] );
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Scontainers
|
||||
{
|
||||
public static function container_delete()
|
||||
{
|
||||
if ( \admin\factory\Scontainers::container_delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Kontener został usunięty.' );
|
||||
header( 'Location: /admin/scontainers/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function container_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kontenera wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\Scontainers::container_save( $values['id'], $values['title'], $values['text'], $values['status'], $values['show_title'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Kontener został zapisany.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function container_edit()
|
||||
{
|
||||
return \admin\view\Scontainers::container_edit(
|
||||
\admin\factory\Scontainers::container_details(
|
||||
\S::get( 'id' )
|
||||
),
|
||||
\admin\factory\Languages::languages_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \admin\view\Scontainers::containers_list();
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
|
||||
class Settings
|
||||
{
|
||||
static public function clear_cache()
|
||||
{
|
||||
\S::delete_dir( '../temp/' );
|
||||
\S::delete_dir( '../thumbs/' );
|
||||
|
||||
$redis = \RedisConnection::getInstance() -> getConnection();
|
||||
if ( $redis )
|
||||
$redis -> flushAll();
|
||||
|
||||
\S::alert( 'Cache został wyczyszczony.' );
|
||||
header( 'Location: /admin/dashboard/main_view/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function settings_save()
|
||||
{
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
$settings = \admin\factory\Settings::settings_details( true );
|
||||
|
||||
$response = \admin\factory\Settings::settings_save(
|
||||
$values['firm_name'], $values['firm_adress'], $values['additional_info'], $values['contact_form'], $values['contact_email'], $values['email_host'],
|
||||
$values['email_port'], $values['email_login'], $values['email_password'], $values['google_maps'], $values['facebook_link'], $values['statistic_code'], $values['htaccess'],
|
||||
$values['robots'], $values['shop_bank_account_info'], $values['update'], $values['boot_animation'], $settings['newsletter_header'], $settings['newsletter_footer'], $values['hotpay_api']
|
||||
);
|
||||
|
||||
\admin\factory\Settings::settings_update( 'devel', $values['devel'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'ssl', $values['ssl'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'htaccess_cache', $values['htaccess_cache'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'free_delivery', $values['free_delivery'] );
|
||||
\admin\factory\Settings::settings_update( 'przelewy24_sandbox', $values['przelewy24_sandbox'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'przelewy24_merchant_id', $values['przelewy24_merchant_id'] );
|
||||
\admin\factory\Settings::settings_update( 'przelewy24_crc_key', $values['przelewy24_crc_key'] );
|
||||
\admin\factory\Settings::settings_update( 'update_key', $values['update_key'] );
|
||||
\admin\factory\Settings::settings_update( 'tpay_id', $values['tpay_id'] );
|
||||
\admin\factory\Settings::settings_update( 'tpay_sandbox', $values['tpay_sandbox'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'tpay_security_code', $values['tpay_security_code'] );
|
||||
\admin\factory\Settings::settings_update( 'piksel', $values['piksel'] );
|
||||
\admin\factory\Settings::settings_update( 'generate_webp', $values['generate_webp'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'lazy_loading', $values['lazy_loading'] == 'on' ? 1 : 0 );
|
||||
\admin\factory\Settings::settings_update( 'orlen_paczka_map_token', $values['orlen_paczka_map_token'] );
|
||||
\admin\factory\Settings::settings_update( 'google_tag_manager_id', $values['google_tag_manager_id'] );
|
||||
\admin\factory\Settings::settings_update( 'infinitescroll', $values['infinitescroll'] == 'on' ? 1 : 0 );
|
||||
// own_gtm_js
|
||||
\admin\factory\Settings::settings_update( 'own_gtm_js', $values['own_gtm_js'] );
|
||||
// own_gtm_html
|
||||
\admin\factory\Settings::settings_update( 'own_gtm_html', $values['own_gtm_html'] );
|
||||
|
||||
foreach ( $values[ 'warehouse_message_zero'] as $key => $val )
|
||||
\admin\factory\Settings::settings_update( 'warehouse_message_zero_' . $key, $val );
|
||||
|
||||
foreach ( $values[ 'warehouse_message_nonzero'] as $key => $val )
|
||||
\admin\factory\Settings::settings_update( 'warehouse_message_nonzero_' . $key, $val );
|
||||
|
||||
\S::delete_dir( '../temp/' );
|
||||
\S::htacces();
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function view()
|
||||
{
|
||||
return \Tpl::view( 'settings/settings', [
|
||||
'languages' => \admin\factory\Languages::languages_list(),
|
||||
'settings' => \admin\factory\Settings::settings_details()
|
||||
] );
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopAttribute
|
||||
{
|
||||
static public function attribute_value_tpl()
|
||||
{
|
||||
$html = \Tpl::view( 'shop-attribute/_partials/value', [
|
||||
'i' => \S::get( 'i' ),
|
||||
'value' => \S::get( 'value' ),
|
||||
'languages' => \S::get( 'languages' ),
|
||||
'attribute' => \S::get( 'attribute' ),
|
||||
] );
|
||||
echo $html;
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function values_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania wartości atrybutu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\ShopAttribute::values_save( (int) \S::get( 'attribute_id' ), $values['name'], $values['value'], $values['ids'], $values['default_value'], $values['impact_on_the_price'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Wartości atrybutu zostały zapisane.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function values_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-attribute/values-edit', [
|
||||
'attribute' => \admin\factory\ShopAttribute::attribute_details( (int) \S::get( 'attribute-id' ) ),
|
||||
'values' => \admin\factory\ShopAttribute::get_attribute_values( (int) \S::get( 'attribute-id' ) ),
|
||||
'languages' => \admin\factory\Languages::languages_list()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function delete_attribute()
|
||||
{
|
||||
if ( \admin\factory\ShopAttribute::delete_attribute( (int) \S::get( 'id' ) ) )
|
||||
\S::alert( 'Atrybut został usunięty.' );
|
||||
|
||||
header( 'Location: /admin/shop_attribute/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function attribute_save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania atrybutu wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\ShopAttribute::attribute_save( (int) $values['id'], $values['name'], $values['status'] == 'on' ? 1 : 0, (int) $values['type'], (int) $values['o'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Atrybut został zapisany.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function attribute_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-attribute/attribute-edit', [
|
||||
'attribute' => \admin\factory\ShopAttribute::attribute_details( (int) \S::get( 'id' ) ),
|
||||
'languages' => \admin\factory\Languages::languages_list()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'shop-attribute/attributes-list' );
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopCategory
|
||||
{
|
||||
public static function category_products()
|
||||
{
|
||||
return \Tpl::view( 'shop-category/category-products', [
|
||||
'category_id' => \S::get( 'id' ),
|
||||
'products' => \admin\factory\ShopCategory::category_products( (int)\S::get( 'id' ) )
|
||||
] );
|
||||
}
|
||||
|
||||
public static function category_url_browser() {
|
||||
echo \Tpl::view( 'shop-category/category-browse-list', [
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'level' => 0,
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function category_delete()
|
||||
{
|
||||
if ( \admin\factory\ShopCategory::category_delete( \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;
|
||||
}
|
||||
|
||||
static public function save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kategorii wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\ShopCategory::save(
|
||||
$values['id'], $values['title'], $values['text'], $values['text_hidden'], $values['seo_link'], $values['meta_title'], $values['meta_description'], $values['meta_keywords'], $values['parent_id'], $values['status'],
|
||||
$values['noindex'], $values['category_title'], $values['sort_type'], $values['additional_text'], $values['view_subcategories']
|
||||
) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Kategoria została zapisana.', 'id' => $id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function category_edit()
|
||||
{
|
||||
return \admin\view\ShopCategory::category_edit(
|
||||
\admin\factory\ShopCategory::category_details(
|
||||
\S::get( 'id' )
|
||||
),
|
||||
\S::get( 'pid' ),
|
||||
\admin\factory\Languages::languages_list()
|
||||
);
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'shop-category/categories-list', [
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'level' => 0,
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopClients
|
||||
{
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view(
|
||||
'shop-clients/view-list'
|
||||
);
|
||||
}
|
||||
|
||||
public static function clients_details()
|
||||
{
|
||||
$query_string = $_SERVER['REDIRECT_QUERY_STRING'];
|
||||
parse_str($query_string, $query_array);
|
||||
|
||||
$orders_info = \admin\factory\ShopClients::get_order_all_info( $query_array['name'], $query_array['surname'], $query_array['email'] );
|
||||
|
||||
return \Tpl::view('shop-clients/clients-details', [
|
||||
'name' => $query_array['name'],
|
||||
'surname' => $query_array['surname'],
|
||||
'email' => $query_array['email'],
|
||||
'total_spent' => $query_array['total_spent'],
|
||||
'orders_info' => $orders_info
|
||||
]);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class ShopCoupon
|
||||
{
|
||||
public static function coupon_delete()
|
||||
{
|
||||
$coupon = new \shop\Coupon( (int)\S::get( 'id' ) );
|
||||
if ( $coupon -> delete() )
|
||||
\S::alert( 'Kupon został usunięty.' );
|
||||
header( 'Location: /admin/shop_coupon/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function coupon_save()
|
||||
{
|
||||
$response = ['status' => 'error', 'msg' => 'Podczas zapisywania kuponu wystąpił błąd. Proszę spróbować ponownie.'];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $values['categories'] != null )
|
||||
$categories = is_array( $values['categories'] ) ? json_encode( $values['categories'] ) : json_encode( [ $values['categories'] ] );
|
||||
else
|
||||
$categories = null;
|
||||
|
||||
if ( \admin\factory\ShopCoupon::save(
|
||||
$values['id'],
|
||||
$values['name'],
|
||||
$values['status'] == 'on' ? 1 : 0,
|
||||
$values['send'] == 'on' ? 1 : 0,
|
||||
$values['used'] == 'on' ? 1 : 0,
|
||||
$values['type'],
|
||||
$values['amount'],
|
||||
$values['one_time'] == 'on' ? 1 : 0,
|
||||
$values['include_discounted_product'] == 'on' ? 1 : 0,
|
||||
$categories
|
||||
)
|
||||
)
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Kupon został zapisany.', 'id' => $values['id'] ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function coupon_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-coupon/coupon-edit', [
|
||||
'coupon' => \admin\factory\ShopCoupon::details( (int)\S::get( 'id' ) ),
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'shop-coupon/view-list' );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopOrder
|
||||
{
|
||||
static public function send_order_to_apilo()
|
||||
{
|
||||
$order_id = \S::get( 'order_id' );
|
||||
if ( \admin\factory\ShopOrder::send_order_to_apilo( $order_id ) ) {
|
||||
\S::alert( 'Zamówienie zostanie wysłane ponownie do apilo.com' );
|
||||
} else {
|
||||
\S::alert( 'Wystąpił błąd podczas wysyłania zamówienia do apilo.com' );
|
||||
}
|
||||
header( 'Location: /admin/shop_order/order_details/order_id=' . $order_id );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function order_resend_confirmation_email()
|
||||
{
|
||||
$order = new \shop\Order( (int)\S::get( 'order_id' ) );
|
||||
$response = $order -> order_resend_confirmation_email();
|
||||
|
||||
echo json_encode( [ 'result' => $response ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function notes_save()
|
||||
{
|
||||
\shop\Order::notes_save( \S::get( 'order_id' ), \S::get( 'notes' ) );
|
||||
}
|
||||
|
||||
static public function order_save()
|
||||
{
|
||||
if ( \shop\Order::order_save_by_admin(
|
||||
\S::get( 'order_id' ), \S::get( 'client_name' ), \S::get( 'client_surname' ), \S::get( 'client_street' ), \S::get( 'client_postal_code' ), \S::get( 'client_city' ), \S::get( 'client_email' ), \S::get( 'firm_name' ), \S::get( 'firm_street' ), \S::get( 'firm_postal_code' ), \S::get( 'firm_city' ), \S::get( 'firm_nip' ), \S::get( 'transport_id' ), \S::get( 'inpost_paczkomat' ), \S::get( 'payment_method_id' )
|
||||
) )
|
||||
\S::alert( 'Zamówienie zostało zapisane.' );
|
||||
|
||||
header( 'Location: /admin/shop_order/order_details/order_id=' . \S::get( 'order_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function order_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-order/order-edit', [
|
||||
'order' => new \shop\Order( (int)\S::get( 'order_id' ) ),
|
||||
'order_statuses' => \shop\Order::order_statuses(),
|
||||
'transport' => \shop\Transport::transport_list(),
|
||||
'payment_methods' => \shop\PaymentMethod::method_list()
|
||||
] );
|
||||
}
|
||||
|
||||
public static function order_details()
|
||||
{
|
||||
$order = new \shop\Order( (int)\S::get( 'order_id' ) );
|
||||
$coupon = $order -> coupon_id ? new \shop\Coupon( $order -> coupon_id ) : null;
|
||||
|
||||
return \Tpl::view( 'shop-order/order-details', [
|
||||
'order' => $order,
|
||||
'coupon' => $coupon,
|
||||
'order_statuses' => \shop\Order::order_statuses(),
|
||||
'next_order_id' => \admin\factory\ShopOrder::next_order_id( (int)\S::get( 'order_id' ) ),
|
||||
'prev_order_id' => \admin\factory\ShopOrder::prev_order_id( (int)\S::get( 'order_id' ) ),
|
||||
] );
|
||||
}
|
||||
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view(
|
||||
'shop-order/view-list'
|
||||
);
|
||||
}
|
||||
|
||||
public static function order_status_change()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$order = new \shop\Order( (int)\S::get( 'order_id' ) );
|
||||
$response = $order -> update_status( (int)\S::get( 'status' ), \S::get( 'email' ) == 'true' ? 1 : 0 );
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function order_delete()
|
||||
{
|
||||
global $user;
|
||||
|
||||
if ( \shop\Order::order_delete( (int)\S::get( 'id' ) ) )
|
||||
{
|
||||
\S::alert( 'Zamówienie zostało usunięte' );
|
||||
\Log::save_log( 'Usunięcie zamówienia | ID: ' . (int)\S::get( 'id' ), $user['id'] );
|
||||
}
|
||||
|
||||
header( 'Location: /admin/shop_order/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
// set_order_as_unpaid
|
||||
public static function set_order_as_unpaid()
|
||||
{
|
||||
$order = new \shop\Order( (int)\S::get( 'order_id' ) );
|
||||
$order -> set_as_unpaid();
|
||||
header( 'Location: /admin/shop_order/order_details/order_id=' . (int)\S::get( 'order_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// set_order_as_paid
|
||||
public static function set_order_as_paid()
|
||||
{
|
||||
$order = new \shop\Order( (int)\S::get( 'order_id' ) );
|
||||
if ( $order -> set_as_paid() )
|
||||
{
|
||||
$order -> update_status( 4, (int)\S::get( 'send_mail' ) );
|
||||
}
|
||||
header( 'Location: /admin/shop_order/order_details/order_id=' . (int)\S::get( 'order_id' ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
// toggle_trustmate_send
|
||||
public static function toggle_trustmate_send()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
$order_id = (int)\S::get( 'order_id' );
|
||||
$order = $mdb -> get( 'pp_shop_orders', [ 'trustmate_send' ], [ 'id' => $order_id ] );
|
||||
|
||||
$new_value = $order['trustmate_send'] ? 0 : 1;
|
||||
$mdb -> update( 'pp_shop_orders', [ 'trustmate_send' => $new_value ], [ 'id' => $order_id ] );
|
||||
|
||||
echo json_encode( [ 'result' => true, 'trustmate_send' => $new_value ] );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopPaymentMethod
|
||||
{
|
||||
public static function view_list()
|
||||
{
|
||||
return \Tpl::view( 'shop-payment-method/view-list', [
|
||||
'apilo_payment_types_list' => unserialize( \admin\factory\Integrations::apilo_settings( 'payment-types-list' ) ),
|
||||
'sellasist_payment_types_list' => unserialize( \admin\factory\Integrations::sellasist_settings( 'payment_types_list' ) ),
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<?
|
||||
namespace admin\controls;
|
||||
class ShopProducer
|
||||
{
|
||||
static public function delete()
|
||||
{
|
||||
if ( \admin\factory\ShopProducer::delete( \S::get( 'id' ) ) )
|
||||
\S::alert( 'Producent został usunięty' );
|
||||
header( 'Location: /admin/shop_producer/list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania producenta wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $producer_id = \admin\factory\ShopProducer::save( $values['id'], $values['name'], $values['status'] == 'on' ? 1 : 0, $values['img'], $values['description'], $values['data'], $values['meta_title'] ) )
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Producent został zapisany.', 'id' => $producer_id ];
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-producer/edit', [
|
||||
'producer' => \S::get( 'id' ) ? new \shop\Producer( \S::get( 'id' ) ) : null,
|
||||
'languages' => \admin\factory\Languages::languages_list()
|
||||
] );
|
||||
}
|
||||
|
||||
static public function list()
|
||||
{
|
||||
return \Tpl::view( 'shop-producer/list' );
|
||||
}
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopProduct
|
||||
{
|
||||
static public function mass_edit_save()
|
||||
{
|
||||
global $mdb;
|
||||
|
||||
if ( \S::get( 'discount_percent' ) != '' and \S::get( 'products' ) )
|
||||
{
|
||||
$product_details = \admin\factory\ShopProduct::product_details( \S::get( 'products' )[0] );
|
||||
|
||||
$vat = $product_details['vat'];
|
||||
$price_brutto = $product_details['price_brutto'];
|
||||
$price_brutto_promo = $price_brutto - ( $price_brutto * ( \S::get( 'discount_percent' ) / 100 ) );
|
||||
$price_netto = $product_details['price_netto'];
|
||||
$price_netto_promo = $price_netto - ( $price_netto * ( \S::get( 'discount_percent' ) / 100 ) );
|
||||
|
||||
if ( $price_brutto == $price_brutto_promo)
|
||||
$price_brutto_promo = null;
|
||||
|
||||
if ( $price_netto == $price_netto_promo )
|
||||
$price_netto_promo = null;
|
||||
|
||||
$mdb -> update( 'pp_shop_products', [ 'price_brutto_promo' => $price_brutto_promo, 'price_netto_promo' => $price_netto_promo ], [ 'id' => \S::get( 'products' )[0] ] );
|
||||
|
||||
\admin\factory\ShopProduct::update_product_combinations_prices( \S::get( 'products' )[0], $price_netto, $vat, $price_netto_promo );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok', 'price_brutto_promo' => $price_brutto_promo, 'price_brutto' => $price_brutto ] );
|
||||
exit;
|
||||
}
|
||||
echo json_encode( [ 'status' => 'error' ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
// get_products_by_category
|
||||
static public function get_products_by_category() {
|
||||
global $mdb;
|
||||
|
||||
$products = $mdb -> select( 'pp_shop_products_categories', 'product_id', [ 'category_id' => \S::get( 'category_id' ) ] );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok', 'products' => $products ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function mass_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-product/mass-edit', [
|
||||
'products' => \admin\factory\ShopProduct::products_list(),
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'dlang' => \front\factory\Languages::default_language()
|
||||
] );
|
||||
}
|
||||
|
||||
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']
|
||||
) ) {
|
||||
$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/archive/products_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;
|
||||
|
||||
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' => \admin\factory\Languages::languages_list(),
|
||||
'categories' => \admin\factory\ShopCategory::subcategories( null ),
|
||||
'layouts' => \admin\factory\Layouts::layouts_list(),
|
||||
'products' => \admin\factory\ShopProduct::products_list(),
|
||||
'dlang' => \front\factory\Languages::default_language(),
|
||||
'sets' => \shop\ProductSet::sets_list(),
|
||||
'producers' => \admin\factory\ShopProducer::all(),
|
||||
'units' => \admin\factory\Dictionaries::all_units(),
|
||||
'user' => $user
|
||||
] );
|
||||
}
|
||||
|
||||
// ajax_load_products ARCHIVE
|
||||
static public function ajax_load_products_archive()
|
||||
{
|
||||
$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_archive( \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( 'archive/products-list-table', [
|
||||
'products' => $products['products'],
|
||||
'current_page' => \S::get( 'current_page' ),
|
||||
] )
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
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' ),
|
||||
'baselinker_enabled' => \admin\factory\Integrations::baselinker_settings( 'enabled' ),
|
||||
'apilo_enabled' => \admin\factory\Integrations::apilo_settings( 'enabled' ),
|
||||
'sellasist_enabled' => \admin\factory\Integrations::sellasist_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 ),
|
||||
'baselinker_enabled' => \admin\factory\Integrations::baselinker_settings( 'enabled' ),
|
||||
'apilo_enabled' => \admin\factory\Integrations::apilo_settings( 'enabled' ),
|
||||
'sellasist_enabled' => \admin\factory\Integrations::sellasist_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()
|
||||
{
|
||||
return \Tpl::view( 'shop-product/product-combination', [
|
||||
'product' => \admin\factory\ShopProduct::product_details( (int) \S::get( 'product_id' ) ),
|
||||
'attributes' => \admin\factory\ShopAttribute::get_attributes_list(),
|
||||
'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;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
class ShopProductSets
|
||||
{
|
||||
|
||||
static public function set_delete()
|
||||
{
|
||||
if ( \shop\ProductSet::set_delete( \S::get( 'id' ) ) )
|
||||
\S::set_message( 'Komplet produktów usunięty.' );
|
||||
else
|
||||
\S::alert( 'Podczas usuwania kompletu produktów wystąpił błąd. Proszę spróbować ponownie' );
|
||||
header( 'Location: /admin/shop_product_sets/view_list/' );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function save()
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania kompletu produktów wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$values = json_decode( \S::get( 'values' ), true );
|
||||
|
||||
if ( $id = \admin\factory\ShopProductSet::save(
|
||||
(int)$values['id'], $values['name'], (string) $values['status'], $values['set_products_id']
|
||||
) ) {
|
||||
$response = [ 'status' => 'ok', 'msg' => 'Komplet produktów został zapisany.', 'id' => $id ];
|
||||
}
|
||||
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
static public function set_edit()
|
||||
{
|
||||
return \Tpl::view( 'shop-product-sets/set-edit', [
|
||||
'set' => new \shop\ProductSet( (int) \S::get( 'id' ) ),
|
||||
'products' => \admin\factory\ShopProduct::products_list()
|
||||
] );
|
||||
}
|
||||
|
||||
static public function view_list()
|
||||
{
|
||||
return \Tpl::view( 'shop-product-sets/view-list' );
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user