first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

448
admin/sliders.php Normal file
View File

@@ -0,0 +1,448 @@
<?php
// ##############################################################
// TENTO SKRIPT NAPROGRAMOVALO STUDIO wpj s.r.o.
// UZIVANI TOHOTO DILA JE MOZNE POUZE SE SOUHLASEM AUTORA
// KONTAKT: joe@wpj.cz
// ##############################################################
use KupShop\CatalogBundle\Util\SectionUtil;
use KupShop\ContentBundle\Util\ImageLocator;
use KupShop\ContentBundle\Util\SliderUtil;
use KupShop\I18nBundle\Translations\SlidersImagesTranslation;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use Query\Operator;
use Query\QueryBuilder;
class Sliders extends Window
{
public const ON_PAGE = 20;
protected SectionUtil $sectionUtil;
protected SliderUtil $sliderUtil;
public function __construct()
{
$this->sectionUtil = ServiceContainer::getService(SectionUtil::class);
$this->sliderUtil = ServiceContainer::getService(SliderUtil::class);
}
public function get_vars()
{
$ID = $this->getID();
$vars = parent::get_vars();
$pageVars = getVal('body', $vars);
$pageVars['ID'] = $ID;
$pageVars['data']['default_size'] = getVal('size', $GLOBALS['cfg']['Modules']['sliders'], [780, 400]);
if ($ID == 1) {
$pageVars['data']['default_size'] = getVal('size_index', $GLOBALS['cfg']['Modules']['sliders'], $pageVars['data']['size']);
}
$pager = $this->createPager();
$SQL = sqlQueryBuilder()
->select('SQL_CALC_FOUND_ROWS si.*')
->from('sliders_images', 'si')
->where(Operator::equals(['si.id_slider' => $ID]))
->andWhere($pager->getSpec())
->orderBy('position', 'ASC')
->execute()->fetchAll();
$total_count = (int) sqlFetchAssoc(sqlQuery('SELECT FOUND_ROWS() as total_count'))['total_count'];
$pager->setTotal($total_count);
$photos = sqlFetchAll(sqlQueryBuilder()
->select('*')
->from('photos')
->where(Operator::inIntArray(array_map(function ($x) { return $x['id_photo']; }, $SQL), 'id'))
->execute(), 'id');
$imageLocator = ServiceContainer::getService(ImageLocator::class);
$images = [];
foreach ($SQL as $row) {
$row['img'] = $row['id_photo'] ? $imageLocator->getImage($photos[$row['id_photo']]) : null;
$row['active'] = $this->isSliderActive($row);
$this->unserializeCustomData($row);
$row['sliderTranslationsFigure'] = $this->getTranslationUtil()?->getTranslationsFigure(SlidersImagesTranslation::class, $row['id']);
$images[] = $row;
}
$pageVars['images'] = $images;
if (findModule(\Modules::PRODUCTS_SECTIONS)) {
$pageVars['data']['slider_sections'] = $this->fetchSliderSections((int) $ID);
}
$this->unserializeCustomData($pageVars['data']);
$vars['body'] = $pageVars;
$vars['pager'] = $pager;
return $vars;
}
public function createPager()
{
$pager = new \Pager();
$pager->setOnPage(self::ON_PAGE);
$pager->setPageNumber((int) getVal('page', null, 1));
$pager->setUrl($_SERVER['REQUEST_URI']);
return $pager;
}
public function getData()
{
$data = parent::getData();
if (getVal('Submit')) {
$this->serializeCustomData($data);
}
return $data;
}
public function handleUpdate()
{
$OLD_ID = $this->getID();
$SQL = parent::handleUpdate();
$ID = $this->getID();
if ($duplicate = $this->isDuplicate()) {
// duplicate superuser fields (sizes)
sqlQueryBuilder()
->update('sliders', 's')
->join('s', 'sliders', 'so', 'so.id = :oldId')
->set('s.size', 'so.size')
->set('s.size_tablet', 'so.size_tablet')
->set('s.size_mobile', 'so.size_mobile')
->where(Operator::equals(['s.id' => $this->getID()]))
->setParameter('oldId', $OLD_ID)
->execute();
// duplicate sliders
$duplicate_sliders = sqlFetchAll($this->selectSQL('sliders_images', ['id_slider' => $OLD_ID]), 'id');
foreach ($duplicate_sliders as &$img) {
$img = $this->insertSlidersImage($img);
}
}
$data = $this->getData();
$sliders = getVal('images', $data, []);
krsort($sliders);
foreach ($sliders as $id => &$img) {
$photoId = empty($img['id_photo']) ? null : $img['id_photo'];
if (!empty($img['delete']) || !$id) {
if ($id > 0) {
$delete_id = $img['id'];
if ($duplicate) {
$delete_id = $duplicate_sliders[$img['id']]['id'] ?? $img['id'];
}
$this->deleteSQL('sliders_images', ['id' => $delete_id, 'id_slider' => $ID]);
}
continue;
}
$img['date_from'] = ($img['date_from'] == '') ? null : $this->prepareDateTime($img['date_from']);
$img['date_to'] = ($img['date_to'] == '') ? null : $this->prepareDateTime($img['date_to']);
$this->serializeCustomData($img);
$uploadedImages = $this->getUploadedImages((int) $id);
$translateSlidersFigure = $img['translation_figure'] ?? [];
unset($img['translation_figure']);
if ($id < 0) {
// Skip if desktop image is missing
if (empty($uploadedImages['desktop'])) {
continue;
}
// upload new photos when adding slider
if ($photoId = $this->uploadImages($uploadedImages, null)) {
$img['id_photo'] = $photoId;
$img['position'] = 0;
$img = $this->insertSlidersImage($img);
}
} else {
if ($duplicate) {
if ($duplicate_img = ($duplicate_sliders[$img['id']] ?? null)) {
unset($img['id']);
$duplicate_img = array_replace($duplicate_img, $img);
$this->updateSQL('sliders_images', $duplicate_img, ['id' => $duplicate_img['id']]);
$img = $duplicate_img;
}
} else {
// upload photos to existing slider (upload of additional photo versions)
if ($photoId) {
$this->uploadImages($uploadedImages, $photoId);
}
$this->updateSQL('sliders_images', $img, ['id' => $img['id']]);
}
$this->getTranslationUtil()?->updateTranslationsFigure(SlidersImagesTranslation::class, $img['id'], $translateSlidersFigure);
}
}
if (findModule(\Modules::PRODUCTS_SECTIONS)) {
$this->saveSliderSections(getVal('slider_sections', $data, []));
}
sqlQuery('SELECT @i := 0; UPDATE sliders_images SET position = (select @i := @i + 1) WHERE id_slider=:id ORDER BY position', ['id' => $ID]);
MenuSectionTree::invalidateCache();
clearCache('sliders', true);
if ($page = getVal('page')) {
$_GET['page'] = $page;
}
$this->activityMessage($data['name']);
$this->returnOK($GLOBALS['txt_str']['status']['saved']);
}
public function uploadImages(array $images, ?int $photoId): int
{
$versions = [
'desktop' => Photos::PHOTO_VERSION_DESKTOP,
'tablet' => Photos::PHOTO_VERSION_TABLET,
'mobile' => Photos::PHOTO_VERSION_MOBILE,
];
foreach ($images as $version => $image) {
if (!empty($image)) {
$img = Photos::get($photoId, $versions[$version]);
$img->uploadPhotoOrVideo($image, $image['name']);
if ($photoId === null) {
$photoId = (int) $img->getID();
}
}
}
return $photoId;
}
public function getUploadedImages(int $id): array
{
$images = $_FILES['image'] ?? [];
$result = [];
foreach (['desktop', 'tablet', 'mobile'] as $type) {
$result[$type] = ($images['size'][$id][$type] ?? 0) <= 0 ? [] : [
'name' => $images['name'][$id][$type],
'type' => $images['type'][$id][$type],
'tmp_name' => $images['tmp_name'][$id][$type],
'error' => $images['error'][$id][$type],
'size' => $images['size'][$id][$type],
];
}
return $result;
}
protected function insertSlidersImage($img)
{
unset($img['id']);
$img['id_slider'] = $this->getID();
$this->insertSQL('sliders_images', $img);
$img['id'] = sqlInsertId();
return $img;
}
protected function isSliderActive(array $slider): bool
{
$today = new DateTime();
$dateFrom = $slider['date_from'] ? new DateTime($slider['date_from']) : null;
$dateTo = $slider['date_to'] ? new DateTime($slider['date_to']) : null;
if (($dateFrom === null || $dateFrom < $today) && ($dateTo === null || $dateTo > $today)) {
return true;
}
return false;
}
public function handleDrag()
{
if (($moved_item = getVal('moved_item')) && ($ID = $this->getID())) {
$this->updateSQL('sliders_images', ['position' => 0], ['id' => $moved_item, 'id_slider' => $ID]);
sqlQuery('SELECT @i := 0; UPDATE sliders_images SET position = (select @i := @i + 1) WHERE id_slider=:id ORDER BY position', ['id' => $ID]);
}
$this->returnOK();
}
public function saveSliderSections(array $sliderSections): void
{
$sliderSections = array_map(fn (string $json) => json_decode_strict($json, true), $sliderSections);
sqlGetConnection()->transactional(function () use ($sliderSections) {
$this->sliderUtil->clearSliderSections(idSlider: (int) $this->getID());
$this->sliderUtil->saveSliderPositions($sliderSections, ['id_slider']);
});
}
protected function fetchSliderSections(int $sliderId): array
{
$sectionIds = sqlQueryBuilder()->select('sis.id_section AS id')
->from('sliders_in_sections', 'sis')
->andWhere(Operator::equals(['sis.id_slider' => $sliderId]));
$qb = $this->createSliderSectionsQB()
->andWhere(Operator::inSubquery('s.id', $sectionIds))
->groupBy('s.id_rootsection', 'sis.id_section', 'sis.id_slider', 'sis.position');
$sliderSections = [];
foreach ($qb->execute() as $flatSection) {
$sectionId = $flatSection['id_section'];
if (!isset($sliderSections[$sectionId])) {
$sliderSections[$sectionId] = [
'id' => $sectionId,
'full_path' => $flatSection['full_path'],
'name' => $flatSection['section_name'],
'badges' => $this->getBadges($flatSection),
'positions' => $this->getEmptyPositions(),
'id_rootsection' => $flatSection['id_rootsection'],
'full_position' => $flatSection['full_position'],
'section_position' => $flatSection['section_position'],
];
}
$sliderSections[$sectionId]['positions'][$flatSection['position']] = $flatSection;
}
$orderedSections = [];
foreach ($sliderSections as $section) {
$orderedSections[$section['id_rootsection']][] = $section;
}
foreach ($orderedSections as $orderedSection) {
uasort($orderedSection, function ($a, $b) {
return $a['section_position'] <=> $b['section_position'];
});
}
return array_reduce($orderedSections, fn ($carry, $sections) => array_merge($carry, $sections), []);
}
protected function createSliderSectionsQB(?int $idTopsection = null): QueryBuilder
{
$whereSection = isset($idTopsection) ? 'id_topsection = :idTopSection' : 'id_topsection IS NULL';
$recursiveSections = "
WITH RECURSIVE cte (id, id_rootsection, id_topsection, name, figure, virtual, show_in_search, full_path, position, full_position) as (
SELECT id_section,
id_section AS id_rootsection,
id_topsection,
(SELECT name FROM sections WHERE id = id_section LIMIT 1) as name,
(SELECT figure FROM sections WHERE id = id_section LIMIT 1) as figure,
(SELECT virtual FROM sections WHERE id = id_section LIMIT 1) as virtual,
(SELECT show_in_search FROM sections WHERE id = id_section LIMIT 1) as show_in_search,
(SELECT name FROM sections WHERE id = id_section LIMIT 1) as full_path,
position,
CAST(LPAD(position, 5, 0) AS CHAR(500)) AS full_position
FROM sections_relation
WHERE {$whereSection}
UNION ALL
SELECT sr.id_section,
cte.id_rootsection,
sr.id_topsection,
(SELECT name FROM sections WHERE id = sr.id_section LIMIT 1) as name,
(SELECT figure FROM sections WHERE id = sr.id_section LIMIT 1) as figure,
(SELECT virtual FROM sections WHERE id = sr.id_section LIMIT 1) as virtual,
(SELECT show_in_search FROM sections WHERE id = sr.id_section LIMIT 1) as show_in_search,
CONCAT(cte.full_path,' > ',(SELECT name FROM sections WHERE id = sr.id_section LIMIT 1)) as full_path,
CONCAT(full_position, '/', LPAD(sr.position, 5, 0)),
sr.position
FROM sections_relation sr
INNER JOIN cte
on sr.id_topsection = cte.id
) SELECT * FROM cte";
return sqlQueryBuilder()->select(
's.id AS id_section',
's.name AS section_name',
's.figure',
's.virtual',
's.show_in_search',
's.full_path',
'sis.id_slider',
'sis.position',
'sl.name AS slider_name',
's.id_topsection',
's.position AS section_position',
's.full_position',
's.id_rootsection'
)
->from("({$recursiveSections})", 's')
->leftJoin('s', 'sliders_in_sections', 'sis', 'sis.id_section = s.id')
->leftJoin('sis', 'sliders', 'sl', 'sis.id_slider = sl.id');
}
protected function getBadges(array $section): array
{
if (!$section) {
return [];
}
$badges = $this->sectionUtil->getBadges(
figure: $section['figure'],
virtual: $section['virtual'],
showInSearch: $section['show_in_search'],
);
foreach ($badges as &$badge) {
$badge['title'] = translate($badge['translate_key'], 'sections');
}
return $badges;
}
protected function getEmptyPositions(): array
{
return array_fill_keys(array_keys($this->sliderUtil::getPositions()), null);
}
public function handleAjaxSection(): never
{
$idSection = getVal('id_section');
if (!is_numeric($idSection)) {
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException('id_section is required');
}
$sections = $this->createSliderSectionsQB()
->andWhere(Operator::equals(['s.id' => $idSection]));
$result = [];
foreach ($sections->execute() as $sectionRow) {
if (empty($result)) {
$result = [
'id' => $idSection,
'name' => $sectionRow['section_name'],
'full_path' => $sectionRow['full_path'],
'badges' => $this->getBadges($sectionRow),
'positions' => $this->getEmptyPositions(),
];
}
if (!empty($sectionRow['id_slider'])) {
$result['positions'][$sectionRow['position']] = [
'id_slider' => $sectionRow['id_slider'],
'slider_name' => $sectionRow['slider_name'],
];
}
}
header('Content-Type: application/json');
echo json_encode($result);
exit;
}
}
$sliders = new Sliders();
$sliders->run();