199 lines
8.6 KiB
PHP
199 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace Websmid\CustomHtml\Form\DataHandler;
|
|
|
|
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
|
|
use Websmid\CustomHtml\Config\Config;
|
|
use Websmid\CustomHtml\Model\CustomHtmlBlock;
|
|
use Websmid\CustomHtml\Model\CustomHtmlFaq;
|
|
use Websmid\CustomHtml\Model\CustomHtmlSlide;
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|
use Db;
|
|
use Exception;
|
|
use Validate;
|
|
|
|
final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
|
|
{
|
|
public function create(array $formData)
|
|
{
|
|
$idGroup = (int) $formData['id_ws_customhtml_group'];
|
|
|
|
$block = new CustomHtmlBlock();
|
|
$block->id_ws_customhtml_group = $idGroup;
|
|
$block->title = $formData['title'];
|
|
$block->type = (string) $formData['type'];
|
|
$block->show_title = (int) $formData['show_title'];
|
|
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
|
$block->content = $formData['content'];
|
|
$block->description = $formData['description'] ?? [];
|
|
$block->extra_description = $formData['extra_description'] ?? [];
|
|
$block->link = $formData['link'] ?? [];
|
|
$block->link_text = $formData['link_text'] ?? [];
|
|
$block->alert_type = (string) ($formData['alert_type'] ?? 'info');
|
|
$block->timeout = (int) ($formData['timeout'] ?? 0);
|
|
$block->slider_mode = (string) ($formData['slider_mode'] ?? Config::SLIDER_DEFAULT_MODE);
|
|
$block->slides_per_view_mobile = (float) ($formData['slides_per_view_mobile'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_MOBILE);
|
|
$block->slides_per_view_tablet = (float) ($formData['slides_per_view_tablet'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_TABLET);
|
|
$block->slides_per_view_desktop = (float) ($formData['slides_per_view_desktop'] ?? 0);
|
|
$block->autoplay_delay = (int) ($formData['autoplay_delay'] ?? 0);
|
|
$block->image = $this->saveUploadedImage($formData['image'] ?? null, '');
|
|
$block->active = (int) $formData['active'];
|
|
$block->ordering = $this->getNextOrdering($idGroup);
|
|
|
|
if (!$block->add()) {
|
|
return false;
|
|
}
|
|
|
|
$this->saveFaqs((int) $block->id, $formData['faqs'] ?? []);
|
|
$this->saveSlides((int) $block->id, $formData['slides'] ?? []);
|
|
|
|
return $block->id;
|
|
}
|
|
|
|
public function update($id, array $formData)
|
|
{
|
|
$block = new CustomHtmlBlock((int) $id);
|
|
|
|
if (!Validate::isLoadedObject($block)) {
|
|
return false;
|
|
}
|
|
|
|
$block->id_ws_customhtml_group = (int) $formData['id_ws_customhtml_group'];
|
|
$block->title = $formData['title'];
|
|
$block->type = (string) $formData['type'];
|
|
$block->show_title = (int) $formData['show_title'];
|
|
$block->custom_class = (string) ($formData['custom_class'] ?? '');
|
|
$block->content = $formData['content'];
|
|
$block->description = $formData['description'] ?? [];
|
|
$block->extra_description = $formData['extra_description'] ?? [];
|
|
$block->link = $formData['link'] ?? [];
|
|
$block->link_text = $formData['link_text'] ?? [];
|
|
$block->alert_type = (string) ($formData['alert_type'] ?? 'info');
|
|
$block->timeout = (int) ($formData['timeout'] ?? 0);
|
|
$block->slider_mode = (string) ($formData['slider_mode'] ?? Config::SLIDER_DEFAULT_MODE);
|
|
$block->slides_per_view_mobile = (float) ($formData['slides_per_view_mobile'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_MOBILE);
|
|
$block->slides_per_view_tablet = (float) ($formData['slides_per_view_tablet'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_TABLET);
|
|
$block->slides_per_view_desktop = (float) ($formData['slides_per_view_desktop'] ?? 0);
|
|
$block->autoplay_delay = (int) ($formData['autoplay_delay'] ?? 0);
|
|
$block->image = $this->saveUploadedImage($formData['image'] ?? null, $block->image);
|
|
$block->active = (int) $formData['active'];
|
|
|
|
if (!$block->update()) {
|
|
return false;
|
|
}
|
|
|
|
$this->saveFaqs((int) $id, $formData['faqs'] ?? []);
|
|
$this->saveSlides((int) $id, $formData['slides'] ?? []);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validates and stores an uploaded banner image, replacing the previous
|
|
* one if any. Never trusts the client-supplied filename or MIME header —
|
|
* the real MIME type is detected server-side and a random filename is
|
|
* generated, so nothing from the upload itself ends up on disk.
|
|
*/
|
|
private function saveUploadedImage(?UploadedFile $file, string $currentFilename): string
|
|
{
|
|
if (null === $file) {
|
|
return $currentFilename;
|
|
}
|
|
|
|
if (!$file->isValid()) {
|
|
throw new Exception('The uploaded image could not be read.');
|
|
}
|
|
|
|
if ($file->getSize() > Config::BANNER_IMAGE_MAX_SIZE) {
|
|
throw new Exception('The uploaded image is too large (max 5 MB).');
|
|
}
|
|
|
|
$mimeType = $file->getMimeType();
|
|
$extension = array_search($mimeType, Config::BANNER_IMAGE_ALLOWED_EXTENSIONS, true);
|
|
|
|
if (false === $extension) {
|
|
throw new Exception('The uploaded file is not a supported image type (jpg, png, gif, webp).');
|
|
}
|
|
|
|
$targetDir = CustomHtmlBlock::getBannerImageDir();
|
|
|
|
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
|
|
throw new Exception('Unable to create the banner image directory.');
|
|
}
|
|
|
|
$newFilename = bin2hex(random_bytes(16)) . '.' . $extension;
|
|
$file->move($targetDir, $newFilename);
|
|
|
|
if (!empty($currentFilename) && is_file($targetDir . $currentFilename)) {
|
|
unlink($targetDir . $currentFilename);
|
|
}
|
|
|
|
return $newFilename;
|
|
}
|
|
|
|
private function getNextOrdering(int $idGroup): int
|
|
{
|
|
$maxOrdering = Db::getInstance()->getValue(
|
|
'SELECT MAX(`ordering`) FROM `' . _DB_PREFIX_ . 'ws_customhtml`
|
|
WHERE `id_ws_customhtml_group` = ' . (int) $idGroup
|
|
);
|
|
|
|
return $maxOrdering === false || $maxOrdering === null ? 0 : ((int) $maxOrdering + 1);
|
|
}
|
|
|
|
private function saveFaqs(int $idBlock, array $faqs): void
|
|
{
|
|
CustomHtmlFaq::deleteByBlockId($idBlock);
|
|
|
|
foreach (array_values($faqs) as $position => $faqData) {
|
|
$question = $faqData['question'] ?? [];
|
|
|
|
if (empty(array_filter($question, static function ($value) {
|
|
return trim((string) $value) !== '';
|
|
}))) {
|
|
continue;
|
|
}
|
|
|
|
$faq = new CustomHtmlFaq();
|
|
$faq->id_ws_customhtml = $idBlock;
|
|
$faq->question = $question;
|
|
$faq->answer = $faqData['answer'] ?? [];
|
|
$faq->ordering = $position;
|
|
$faq->add();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Slides are fully replaced on every save, like FAQ items — except a
|
|
* slide's image can't round-trip through its file input, so the previous
|
|
* filename is looked up by the slide's hidden id and reused whenever no
|
|
* new file was uploaded for that row.
|
|
*/
|
|
private function saveSlides(int $idBlock, array $slides): void
|
|
{
|
|
$existingImages = [];
|
|
|
|
foreach (CustomHtmlSlide::getByBlockId($idBlock) as $existingSlide) {
|
|
$existingImages[(int) $existingSlide['id_ws_customhtml_slide']] = $existingSlide['image'];
|
|
}
|
|
|
|
CustomHtmlSlide::deleteByBlockId($idBlock);
|
|
|
|
foreach (array_values($slides) as $position => $slideData) {
|
|
$existingId = (int) ($slideData['id'] ?? 0);
|
|
$existingImage = $existingImages[$existingId] ?? '';
|
|
|
|
$slide = new CustomHtmlSlide();
|
|
$slide->id_ws_customhtml = $idBlock;
|
|
$slide->title = $slideData['title'] ?? [];
|
|
$slide->description = $slideData['description'] ?? [];
|
|
$slide->extra_description = $slideData['extra_description'] ?? [];
|
|
$slide->link = $slideData['link'] ?? [];
|
|
$slide->link_text = $slideData['link_text'] ?? [];
|
|
$slide->image = $this->saveUploadedImage($slideData['image'] ?? null, $existingImage);
|
|
$slide->ordering = $position;
|
|
$slide->add();
|
|
}
|
|
}
|
|
}
|